Matrix generation cleanup

1. Convert a CompressedRowSparseMatrix constructor which
takes a TripletSparseMatrix as input into a factory method
which allows the input to be transposed.

2. Move the random matrix creation routine for CompressedRowSparseMatrix
from being a standalone function to a static method.

3. Add a corresponding random matrix generation static method to
TripletSparseMatrix.

4. Add a new constructor to TripletSparseMatrix, which takes as input
the row, col and values arrays.

Change-Id: Iec7b184646818f432a5e6822bea3b2f3128a82aa
diff --git a/internal/ceres/compressed_row_sparse_matrix.cc b/internal/ceres/compressed_row_sparse_matrix.cc
index 9c438f6..ccb85bc 100644
--- a/internal/ceres/compressed_row_sparse_matrix.cc
+++ b/internal/ceres/compressed_row_sparse_matrix.cc
@@ -116,6 +116,43 @@
   }
   transpose_rows[0] = 0;
 }
+
+void AddRandomBlock(const int num_rows,
+                    const int num_cols,
+                    const int row_block_begin,
+                    const int col_block_begin,
+                    std::vector<int>* rows,
+                    std::vector<int>* cols,
+                    std::vector<double>* values) {
+  for (int r = 0; r < num_rows; ++r) {
+    for (int c = 0; c < num_cols; ++c) {
+      rows->push_back(row_block_begin + r);
+      cols->push_back(col_block_begin + c);
+      values->push_back(RandNormal());
+    }
+  }
+}
+
+void AddRandomSymmetricBlock(const int num_rows,
+                             const int row_block_begin,
+                             std::vector<int>* rows,
+                             std::vector<int>* cols,
+                             std::vector<double>* values) {
+  for (int r = 0; r < num_rows; ++r) {
+    for (int c = r; c < num_rows; ++c) {
+      const double v = RandNormal();
+      rows->push_back(row_block_begin + r);
+      cols->push_back(row_block_begin + c);
+      values->push_back(v);
+      if (c != r) {
+        cols->push_back(row_block_begin + r);
+        rows->push_back(row_block_begin + c);
+        values->push_back(v);
+      }
+    }
+  }
+}
+
 }  // namespace
 
 // This constructor gives you a semi-initialized CompressedRowSparseMatrix.
@@ -136,47 +173,70 @@
                  cols_.size() * sizeof(double);  // NOLINT
 }
 
-CompressedRowSparseMatrix::CompressedRowSparseMatrix(
-    const TripletSparseMatrix& m) {
-  num_rows_ = m.num_rows();
-  num_cols_ = m.num_cols();
-  storage_type_ = UNSYMMETRIC;
+CompressedRowSparseMatrix* CompressedRowSparseMatrix::FromTripletSparseMatrix(
+    const TripletSparseMatrix& input) {
+  return CompressedRowSparseMatrix::FromTripletSparseMatrix(input, false);
+}
 
-  rows_.resize(num_rows_ + 1, 0);
-  cols_.resize(m.num_nonzeros(), 0);
-  values_.resize(m.max_num_nonzeros(), 0.0);
+CompressedRowSparseMatrix*
+CompressedRowSparseMatrix::FromTripletSparseMatrixTransposed(
+    const TripletSparseMatrix& input) {
+  return CompressedRowSparseMatrix::FromTripletSparseMatrix(input, true);
+}
 
-  // index is the list of indices into the TripletSparseMatrix m.
-  vector<int> index(m.num_nonzeros(), 0);
-  for (int i = 0; i < m.num_nonzeros(); ++i) {
+CompressedRowSparseMatrix* CompressedRowSparseMatrix::FromTripletSparseMatrix(
+    const TripletSparseMatrix& input, bool transpose) {
+  int num_rows = input.num_rows();
+  int num_cols = input.num_cols();
+  const int* rows = input.rows();
+  const int* cols = input.cols();
+  const double* values = input.values();
+
+  if (transpose) {
+    std::swap(num_rows, num_cols);
+    std::swap(rows, cols);
+  }
+
+  // index is the list of indices into the TripletSparseMatrix input.
+  vector<int> index(input.num_nonzeros(), 0);
+  for (int i = 0; i < input.num_nonzeros(); ++i) {
     index[i] = i;
   }
 
   // Sort index such that the entries of m are ordered by row and ties
   // are broken by column.
-  sort(index.begin(), index.end(), RowColLessThan(m.rows(), m.cols()));
+  std::sort(index.begin(), index.end(), RowColLessThan(rows, cols));
 
-  VLOG(1) << "# of rows: " << num_rows_ << " # of columns: " << num_cols_
-          << " max_num_nonzeros: " << cols_.size() << ". Allocating "
-          << ((num_rows_ + 1) * sizeof(int) +  // NOLINT
-              cols_.size() * sizeof(int) +     // NOLINT
-              cols_.size() * sizeof(double));  // NOLINT
+  VLOG(1) << "# of rows: " << num_rows << " # of columns: " << num_cols
+          << " num_nonzeros: " << input.num_nonzeros() << ". Allocating "
+          << ((num_rows + 1) * sizeof(int) +           // NOLINT
+              input.num_nonzeros() * sizeof(int) +     // NOLINT
+              input.num_nonzeros() * sizeof(double));  // NOLINT
+
+  CompressedRowSparseMatrix* output =
+      new CompressedRowSparseMatrix(num_rows, num_cols, input.num_nonzeros());
 
   // Copy the contents of the cols and values array in the order given
   // by index and count the number of entries in each row.
-  for (int i = 0; i < m.num_nonzeros(); ++i) {
+  int* output_rows = output->mutable_rows();
+  int* output_cols = output->mutable_cols();
+  double* output_values = output->mutable_values();
+
+  output_rows[0] = 0;
+  for (int i = 0; i < index.size(); ++i) {
     const int idx = index[i];
-    ++rows_[m.rows()[idx] + 1];
-    cols_[i] = m.cols()[idx];
-    values_[i] = m.values()[idx];
+    ++output_rows[rows[idx] + 1];
+    output_cols[i] = cols[idx];
+    output_values[i] = values[idx];
   }
 
   // Find the cumulative sum of the row counts.
-  for (int i = 1; i < num_rows_ + 1; ++i) {
-    rows_[i] += rows_[i - 1];
+  for (int i = 1; i < num_rows + 1; ++i) {
+    output_rows[i] += output_rows[i - 1];
   }
 
-  CHECK_EQ(num_nonzeros(), m.num_nonzeros());
+  CHECK_EQ(output->num_nonzeros(), input.num_nonzeros());
+  return output;
 }
 
 CompressedRowSparseMatrix::CompressedRowSparseMatrix(const double* diagonal,
@@ -585,8 +645,8 @@
   CHECK_GT(product.size(), 0);
 
   vector<int> row_nnz;
-  CompressedRowSparseMatrix* matrix =
-      CreateOuterProductMatrix(num_cols, storage_type, blocks, product, &row_nnz);
+  CompressedRowSparseMatrix* matrix = CreateOuterProductMatrix(
+      num_cols, storage_type, blocks, product, &row_nnz);
 
   const vector<int>& block_offsets = matrix->block_offsets();
 
@@ -697,10 +757,10 @@
 
 CompressedRowSparseMatrix*
 CompressedRowSparseMatrix::CreateOuterProductMatrixAndProgram(
-      const CompressedRowSparseMatrix& m,
-      const CompressedRowSparseMatrix::StorageType storage_type,
-      vector<int>* program) {
-  CHECK(storage_type ==  LOWER_TRIANGULAR || storage_type == UPPER_TRIANGULAR);
+    const CompressedRowSparseMatrix& m,
+    const CompressedRowSparseMatrix::StorageType storage_type,
+    vector<int>* program) {
+  CHECK(storage_type == LOWER_TRIANGULAR || storage_type == UPPER_TRIANGULAR);
   CHECK_NOTNULL(program)->clear();
   CHECK_GT(m.num_nonzeros(), 0)
       << "Congratulations, you found a bug in Ceres. Please report it.";
@@ -778,8 +838,8 @@
     const CompressedRowSparseMatrix& m,
     const vector<int>& program,
     CompressedRowSparseMatrix* result) {
-  CHECK(result->storage_type() ==  LOWER_TRIANGULAR ||
-        result->storage_type() ==  UPPER_TRIANGULAR);
+  CHECK(result->storage_type() == LOWER_TRIANGULAR ||
+        result->storage_type() == UPPER_TRIANGULAR);
   result->SetZero();
   double* values = result->mutable_values();
   const int* rows = result->rows();
@@ -796,7 +856,6 @@
 #define COL_BLOCK1 (crsb_cols[idx1])
 #define COL_BLOCK2 (crsb_cols[idx2])
 
-
   // Iterate row blocks.
   for (int row_block = 0, m_row_begin = 0; row_block < row_blocks.size();
        m_row_begin += row_blocks[row_block++]) {
@@ -853,8 +912,19 @@
   CHECK_EQ(cursor, program.size());
 }
 
-CompressedRowSparseMatrix* CreateRandomCompressedRowSparseMatrix(
-    const RandomMatrixOptions& options) {
+CompressedRowSparseMatrix* CompressedRowSparseMatrix::CreateRandomMatrix(
+    const CompressedRowSparseMatrix::RandomMatrixOptions& options) {
+  CHECK_GT(options.num_row_blocks, 0);
+  CHECK_GT(options.min_row_block_size, 0);
+  CHECK_GT(options.max_row_block_size, 0);
+  CHECK_LE(options.min_row_block_size, options.max_row_block_size);
+  CHECK_GT(options.num_col_blocks, 0);
+  CHECK_GT(options.min_col_block_size, 0);
+  CHECK_GT(options.max_col_block_size, 0);
+  CHECK_LE(options.min_col_block_size, options.max_col_block_size);
+  CHECK_GT(options.block_density, 0.0);
+  CHECK_LE(options.block_density, 1.0);
+
   vector<int> row_blocks;
   vector<int> col_blocks;
 
@@ -889,22 +959,26 @@
   // not what the user wants, so do the matrix generation till we have
   // at least one non-zero entry.
   while (tsm_values.empty()) {
-    int row_block_begin = 0;
     crsb_rows.clear();
     crsb_cols.clear();
+    tsm_rows.clear();
+    tsm_cols.clear();
+    tsm_values.clear();
+
+    int row_block_begin = 0;
     for (int r = 0; r < options.num_row_blocks; ++r) {
       int col_block_begin = 0;
       crsb_rows.push_back(crsb_cols.size());
       for (int c = 0; c < options.num_col_blocks; ++c) {
         // Randomly determine if this block is present or not.
         if (RandDouble() <= options.block_density) {
-          for (int i = 0; i < row_blocks[r]; ++i) {
-            for (int j = 0; j < col_blocks[c]; ++j) {
-              tsm_rows.push_back(row_block_begin + i);
-              tsm_cols.push_back(col_block_begin + j);
-              tsm_values.push_back(RandNormal());
-            }
-          }
+          AddRandomBlock(row_blocks[r],
+                         col_blocks[c],
+                         row_block_begin,
+                         col_block_begin,
+                         &tsm_rows,
+                         &tsm_cols,
+                         &tsm_values);
           // Add the block to the block sparse structure.
           crsb_cols.push_back(c);
         }
@@ -917,21 +991,17 @@
 
   const int num_rows = std::accumulate(row_blocks.begin(), row_blocks.end(), 0);
   const int num_cols = std::accumulate(col_blocks.begin(), col_blocks.end(), 0);
-  const int num_nonzeros = tsm_values.size();
-
-  // Create a TripletSparseMatrix
-  TripletSparseMatrix tsm(num_rows, num_cols, num_nonzeros);
-  std::copy(tsm_rows.begin(), tsm_rows.end(), tsm.mutable_rows());
-  std::copy(tsm_cols.begin(), tsm_cols.end(), tsm.mutable_cols());
-  std::copy(tsm_values.begin(), tsm_values.end(), tsm.mutable_values());
-  tsm.set_num_nonzeros(num_nonzeros);
-
-  // Convert the TripletSparseMatrix to a CompressedRowSparseMatrix.
-  CompressedRowSparseMatrix* matrix = new CompressedRowSparseMatrix(tsm);
+  const bool kDoNotTranspose = false;
+  CompressedRowSparseMatrix* matrix =
+      CompressedRowSparseMatrix::FromTripletSparseMatrix(
+          TripletSparseMatrix(
+              num_rows, num_cols, tsm_rows, tsm_cols, tsm_values),
+          kDoNotTranspose);
   (*matrix->mutable_row_blocks()) = row_blocks;
   (*matrix->mutable_col_blocks()) = col_blocks;
   (*matrix->mutable_crsb_rows()) = crsb_rows;
   (*matrix->mutable_crsb_cols()) = crsb_cols;
+  matrix->set_storage_type(CompressedRowSparseMatrix::UNSYMMETRIC);
   return matrix;
 }
 
diff --git a/internal/ceres/compressed_row_sparse_matrix.h b/internal/ceres/compressed_row_sparse_matrix.h
index bf67737..1e26f7c 100644
--- a/internal/ceres/compressed_row_sparse_matrix.h
+++ b/internal/ceres/compressed_row_sparse_matrix.h
@@ -50,18 +50,33 @@
  public:
   enum StorageType {
     UNSYMMETRIC,
+    // Matrix is assumed to be symmetric but only the lower triangular
+    // part of the matrix is stored.
     LOWER_TRIANGULAR,
+    // Matrix is assumed to be symmetric but only the upper triangular
+    // part of the matrix is stored.
     UPPER_TRIANGULAR
   };
 
-
-  // Build a matrix with the same content as the TripletSparseMatrix
-  // m. TripletSparseMatrix objects are easier to construct
-  // incrementally, so we use them to initialize SparseMatrix
-  // objects.
+  // Create a matrix with the same content as the TripletSparseMatrix
+  // input. We assume that input does not have any repeated
+  // entries.
   //
-  // We assume that m does not have any repeated entries.
-  explicit CompressedRowSparseMatrix(const TripletSparseMatrix& m);
+  // The storage type of the matrix is set to UNSYMMETRIC.
+  //
+  // Caller owns the result.
+  static CompressedRowSparseMatrix* FromTripletSparseMatrix(
+      const TripletSparseMatrix& input);
+
+  // Create a matrix with the same content as the TripletSparseMatrix
+  // input transposed. We assume that input does not have any repeated
+  // entries.
+  //
+  // The storage type of the matrix is set to UNSYMMETRIC.
+  //
+  // Caller owns the result.
+  static CompressedRowSparseMatrix* FromTripletSparseMatrixTransposed(
+      const TripletSparseMatrix& input);
 
   // Use this constructor only if you know what you are doing. This
   // creates a "blank" matrix with the appropriate amount of memory
@@ -74,17 +89,20 @@
   // manually, instead of going via the indirect route of first
   // constructing a TripletSparseMatrix, which leads to more than
   // double the peak memory usage.
+  //
+  // The storage type is set to UNSYMMETRIC.
   CompressedRowSparseMatrix(int num_rows,
                             int num_cols,
                             int max_num_nonzeros);
 
   // Build a square sparse diagonal matrix with num_rows rows and
   // columns. The diagonal m(i,i) = diagonal(i);
+  //
+  // The storage type is set to UNSYMMETRIC
   CompressedRowSparseMatrix(const double* diagonal, int num_rows);
 
-  virtual ~CompressedRowSparseMatrix();
-
   // SparseMatrix interface.
+  virtual ~CompressedRowSparseMatrix();
   virtual void SetZero();
   virtual void RightMultiply(const double* x, double* y) const;
   virtual void LeftMultiply(const double* x, double* y) const;
@@ -145,10 +163,60 @@
   const std::vector<int>& crsb_cols() const { return crsb_cols_; }
   std::vector<int>* mutable_crsb_cols() { return &crsb_cols_; }
 
+  // Create a block diagonal CompressedRowSparseMatrix with the given
+  // block structure. The individual blocks are assumed to be laid out
+  // contiguously in the diagonal array, one block at a time.
+  //
+  // Caller owns the result.
   static CompressedRowSparseMatrix* CreateBlockDiagonalMatrix(
       const double* diagonal,
       const std::vector<int>& blocks);
 
+  // Options struct to control the generation of random block sparse
+  // matrices in compressed row sparse format.
+  //
+  // The random matrix generation proceeds as follows.
+  //
+  // First the row and column block structure is determined by
+  // generating random row and column block sizes that lie within the
+  // given bounds.
+  //
+  // Then we walk the block structure of the resulting matrix, and with
+  // probability block_density detemine whether they are structurally
+  // zero or not. If the answer is no, then we generate entries for the
+  // block which are distributed normally.
+  struct RandomMatrixOptions {
+    RandomMatrixOptions()
+        : num_row_blocks(0),
+          min_row_block_size(0),
+          max_row_block_size(0),
+          num_col_blocks(0),
+          min_col_block_size(0),
+          max_col_block_size(0),
+          block_density(0.0) {
+    }
+
+    int num_row_blocks;
+    int min_row_block_size;
+    int max_row_block_size;
+    int num_col_blocks;
+    int min_col_block_size;
+    int max_col_block_size;
+
+    // 0 < block_density <= 1 is the probability of a block being
+    // present in the matrix. A given random matrix will not have
+    // precisely this density.
+    double block_density;
+  };
+
+  // Create a random CompressedRowSparseMatrix whose entries are
+  // normally distributed and whose structure is determined by
+  // RandomMatrixOptions.
+  //
+  // Caller owns the result.
+  static CompressedRowSparseMatrix* CreateRandomMatrix(
+      const RandomMatrixOptions& options);
+
   // Compute the sparsity structure of the product m.transpose() * m
   // and create a CompressedRowSparseMatrix corresponding to it.
   //
@@ -180,6 +248,10 @@
                                   CompressedRowSparseMatrix* result);
 
  private:
+
+  static CompressedRowSparseMatrix* FromTripletSparseMatrix(
+      const TripletSparseMatrix& input, bool transpose);
+
   int num_rows_;
   int num_cols_;
   std::vector<int> rows_;
@@ -206,45 +278,8 @@
   // carry with it compressed row sparse block information.
   std::vector<int> crsb_rows_;
   std::vector<int> crsb_cols_;
-
-  CERES_DISALLOW_COPY_AND_ASSIGN(CompressedRowSparseMatrix);
 };
 
-// Options struct to control the generation of random block sparse
-// matrices in compressed row sparse format.
-//
-// The random matrix generation proceeds as follows.
-//
-// First the row and column block structure is determined by
-// generating random row and column block sizes that lie within the
-// given bounds.
-//
-// Then we walk the block structure of the resulting matrix, and with
-// probability block_density detemine whether they are structurally
-// zero or not. If the answer is no, then we generate entries for the
-// block which are distributed normally.
-struct RandomMatrixOptions {
-  int num_row_blocks;
-  int min_row_block_size;
-  int max_row_block_size;
-  int num_col_blocks;
-  int min_col_block_size;
-  int max_col_block_size;
-
-  // 0 <= block_density <= 1 is the probability of a block being
-  // present in the matrix. A given random matrix will not have
-  // precisely this density.
-  double block_density;
-};
-
-// Create a random CompressedRowSparseMatrix whose entries are
-// normally distributed and whose structure is determined by
-// RandomMatrixOptions.
-//
-// Caller owns the result.
-CompressedRowSparseMatrix* CreateRandomCompressedRowSparseMatrix(
-    const RandomMatrixOptions& options);
-
 }  // namespace internal
 }  // namespace ceres
 
diff --git a/internal/ceres/compressed_row_sparse_matrix_test.cc b/internal/ceres/compressed_row_sparse_matrix_test.cc
index 66e2390..bb8456e 100644
--- a/internal/ceres/compressed_row_sparse_matrix_test.cc
+++ b/internal/ceres/compressed_row_sparse_matrix_test.cc
@@ -70,7 +70,7 @@
 }
 
 class CompressedRowSparseMatrixTest : public ::testing::Test {
- protected :
+ protected:
   virtual void SetUp() {
     scoped_ptr<LinearLeastSquaresProblem> problem(
         CreateLinearLeastSquaresProblemFromId(1));
@@ -78,7 +78,7 @@
     CHECK_NOTNULL(problem.get());
 
     tsm.reset(down_cast<TripletSparseMatrix*>(problem->A.release()));
-    crsm.reset(new CompressedRowSparseMatrix(*tsm));
+    crsm.reset(CompressedRowSparseMatrix::FromTripletSparseMatrix(*tsm));
 
     num_rows = tsm->num_rows();
     num_cols = tsm->num_cols();
@@ -93,9 +93,11 @@
 
     // With all blocks of size 1, crsb_rows and crsb_cols are equivalent to
     // rows and cols.
-    std::copy(crsm->rows(), crsm->rows() + crsm->num_rows() + 1,
+    std::copy(crsm->rows(),
+              crsm->rows() + crsm->num_rows() + 1,
               std::back_inserter(*crsm->mutable_crsb_rows()));
-    std::copy(crsm->cols(), crsm->cols() + crsm->num_nonzeros(),
+    std::copy(crsm->cols(),
+              crsm->cols() + crsm->num_nonzeros(),
               std::back_inserter(*crsm->mutable_crsb_cols()));
   }
 
@@ -172,9 +174,10 @@
     tsm_appendage.Resize(i, num_cols);
 
     tsm->AppendRows(tsm_appendage);
-    CompressedRowSparseMatrix crsm_appendage(tsm_appendage);
-    crsm->AppendRows(crsm_appendage);
+    scoped_ptr<CompressedRowSparseMatrix> crsm_appendage(
+        CompressedRowSparseMatrix::FromTripletSparseMatrix(tsm_appendage));
 
+    crsm->AppendRows(*crsm_appendage);
     CompareMatrices(tsm.get(), crsm.get());
   }
 }
@@ -219,9 +222,9 @@
   EXPECT_EQ(expected_col_blocks, crsm->col_blocks());
 
   EXPECT_EQ(crsm->crsb_cols().size(),
-             pre_crsb_cols.size() + row_and_column_blocks.size());
+            pre_crsb_cols.size() + row_and_column_blocks.size());
   EXPECT_EQ(crsm->crsb_rows().size(),
-             pre_crsb_rows.size() + row_and_column_blocks.size());
+            pre_crsb_rows.size() + row_and_column_blocks.size());
   for (int i = 0; i < row_and_column_blocks.size(); ++i) {
     EXPECT_EQ(crsm->crsb_rows()[i + pre_crsb_rows.size()],
               pre_crsb_rows.back() + i + 1);
@@ -234,7 +237,6 @@
 
   EXPECT_EQ(crsm->crsb_rows(), pre_crsb_rows);
   EXPECT_EQ(crsm->crsb_cols(), pre_crsb_cols);
-
 }
 
 TEST_F(CompressedRowSparseMatrixTest, ToDenseMatrix) {
@@ -278,8 +280,8 @@
   }
 
   scoped_ptr<CompressedRowSparseMatrix> matrix(
-      CompressedRowSparseMatrix::CreateBlockDiagonalMatrix(
-          diagonal.data(), blocks));
+      CompressedRowSparseMatrix::CreateBlockDiagonalMatrix(diagonal.data(),
+                                                           blocks));
 
   EXPECT_EQ(matrix->num_rows(), 5);
   EXPECT_EQ(matrix->num_cols(), 5);
@@ -351,7 +353,6 @@
   cols[5] = 2;
   cols[6] = 5;
 
-
   rows[2] = 7;
   cols[7] = 0;
   cols[8] = 1;
@@ -394,21 +395,19 @@
 TEST(CompressedRowSparseMatrix, ComputeOuterProduct) {
   // "Randomly generated seed."
   SetRandomState(29823);
-  int kMaxNumRowBlocks = 10;
-  int kMaxNumColBlocks = 10;
-  int kNumTrials = 10;
+  const int kMaxNumRowBlocks = 10;
+  const int kMaxNumColBlocks = 10;
+  const int kNumTrials = 10;
 
   // Create a random matrix, compute its outer product using Eigen and
   // ComputeOuterProduct. Convert both matrices to dense matrices and
   // compare their upper triangular parts.
-  for (int num_row_blocks = 1;
-       num_row_blocks < kMaxNumRowBlocks;
+  for (int num_row_blocks = 1; num_row_blocks < kMaxNumRowBlocks;
        ++num_row_blocks) {
-    for (int num_col_blocks = 1;
-         num_col_blocks < kMaxNumColBlocks;
+    for (int num_col_blocks = 1; num_col_blocks < kMaxNumColBlocks;
          ++num_col_blocks) {
       for (int trial = 0; trial < kNumTrials; ++trial) {
-        RandomMatrixOptions options;
+        CompressedRowSparseMatrix::RandomMatrixOptions options;
         options.num_row_blocks = num_row_blocks;
         options.num_col_blocks = num_col_blocks;
         options.min_row_block_size = 1;
@@ -426,7 +425,7 @@
         VLOG(2) << "block density: " << options.block_density;
 
         scoped_ptr<CompressedRowSparseMatrix> random_matrix(
-            CreateRandomCompressedRowSparseMatrix(options));
+            CompressedRowSparseMatrix::CreateRandomMatrix(options));
 
         Eigen::MappedSparseMatrix<double, Eigen::RowMajor> mapped_random_matrix(
             random_matrix->num_rows(),
@@ -480,5 +479,60 @@
   }
 }
 
+TEST(CompressedRowSparseMatrix, FromTripletSparseMatrix) {
+  TripletSparseMatrix::RandomMatrixOptions options;
+  options.num_rows = 5;
+  options.num_cols = 7;
+  options.density = 0.5;
+
+  const int kNumTrials = 10;
+  for (int i = 0; i < kNumTrials; ++i) {
+    scoped_ptr<TripletSparseMatrix> tsm(
+        TripletSparseMatrix::CreateRandomMatrix(options));
+    scoped_ptr<CompressedRowSparseMatrix> crsm(
+        CompressedRowSparseMatrix::FromTripletSparseMatrix(*tsm));
+
+    Matrix expected;
+    tsm->ToDenseMatrix(&expected);
+    Matrix actual;
+    crsm->ToDenseMatrix(&actual);
+    EXPECT_NEAR((expected - actual).norm() / actual.norm(),
+                0.0,
+                std::numeric_limits<double>::epsilon())
+        << "\nexpected: \n"
+        << expected << "\nactual: \n"
+        << actual;
+  }
+}
+
+TEST(CompressedRowSparseMatrix, FromTripletSparseMatrixTransposed) {
+  TripletSparseMatrix::RandomMatrixOptions options;
+  options.num_rows = 5;
+  options.num_cols = 7;
+  options.density = 0.5;
+
+  const int kNumTrials = 10;
+  for (int i = 0; i < kNumTrials; ++i) {
+    scoped_ptr<TripletSparseMatrix> tsm(
+        TripletSparseMatrix::CreateRandomMatrix(options));
+    scoped_ptr<CompressedRowSparseMatrix> crsm(
+        CompressedRowSparseMatrix::FromTripletSparseMatrixTransposed(*tsm));
+
+    Matrix tmp;
+    tsm->ToDenseMatrix(&tmp);
+    Matrix expected = tmp.transpose();
+    Matrix actual;
+    crsm->ToDenseMatrix(&actual);
+    EXPECT_NEAR((expected - actual).norm() / actual.norm(),
+                0.0,
+                std::numeric_limits<double>::epsilon())
+        << "\nexpected: \n"
+        << expected << "\nactual: \n"
+        << actual;
+  }
+}
+
+// TODO(sameeragarwal) Add tests for the random matrix creation methods.
+
 }  // namespace internal
 }  // namespace ceres
diff --git a/internal/ceres/dynamic_compressed_row_sparse_matrix_test.cc b/internal/ceres/dynamic_compressed_row_sparse_matrix_test.cc
index d27385a..4030142 100644
--- a/internal/ceres/dynamic_compressed_row_sparse_matrix_test.cc
+++ b/internal/ceres/dynamic_compressed_row_sparse_matrix_test.cc
@@ -108,7 +108,7 @@
     tsm->ToDenseMatrix(&dense_from_tsm);
     ASSERT_TRUE((dense.array() == dense_from_tsm.array()).all());
 
-    crsm.reset(new CompressedRowSparseMatrix(*tsm));
+    crsm.reset(CompressedRowSparseMatrix::FromTripletSparseMatrix(*tsm));
     Matrix dense_from_crsm;
     crsm->ToDenseMatrix(&dense_from_crsm);
     ASSERT_TRUE((dense.array() == dense_from_crsm.array()).all());
diff --git a/internal/ceres/schur_complement_solver.cc b/internal/ceres/schur_complement_solver.cc
index 81e0d46..26c0e89 100644
--- a/internal/ceres/schur_complement_solver.cc
+++ b/internal/ceres/schur_complement_solver.cc
@@ -547,15 +547,16 @@
   }
 
   // This is an upper triangular matrix.
-  CompressedRowSparseMatrix crsm(*tsm);
+  scoped_ptr<CompressedRowSparseMatrix> crsm(
+      CompressedRowSparseMatrix::FromTripletSparseMatrix(*tsm));
   // Map this to a column major, lower triangular matrix.
   Eigen::MappedSparseMatrix<double, Eigen::ColMajor> eigen_lhs(
-      crsm.num_rows(),
-      crsm.num_rows(),
-      crsm.num_nonzeros(),
-      crsm.mutable_rows(),
-      crsm.mutable_cols(),
-      crsm.mutable_values());
+      crsm->num_rows(),
+      crsm->num_rows(),
+      crsm->num_nonzeros(),
+      crsm->mutable_rows(),
+      crsm->mutable_cols(),
+      crsm->mutable_values());
   event_logger.AddEvent("ToCompressedRowSparseMatrix");
 
   // Compute symbolic factorization if one does not exist.
diff --git a/internal/ceres/triplet_sparse_matrix.cc b/internal/ceres/triplet_sparse_matrix.cc
index 8df405c..a7df060 100644
--- a/internal/ceres/triplet_sparse_matrix.cc
+++ b/internal/ceres/triplet_sparse_matrix.cc
@@ -35,6 +35,7 @@
 #include "ceres/internal/eigen.h"
 #include "ceres/internal/port.h"
 #include "ceres/internal/scoped_ptr.h"
+#include "ceres/random.h"
 #include "ceres/types.h"
 #include "glog/logging.h"
 
@@ -69,6 +70,27 @@
   AllocateMemory();
 }
 
+TripletSparseMatrix::TripletSparseMatrix(const int num_rows,
+                                         const int num_cols,
+                                         const std::vector<int>& rows,
+                                         const std::vector<int>& cols,
+                                         const std::vector<double>& values)
+    : num_rows_(num_rows),
+      num_cols_(num_cols),
+      max_num_nonzeros_(values.size()),
+      num_nonzeros_(values.size()),
+      rows_(NULL),
+      cols_(NULL),
+      values_(NULL) {
+  // All the sizes should at least be zero
+  CHECK_GE(num_rows, 0);
+  CHECK_GE(num_cols, 0);
+  AllocateMemory();
+  std::copy(rows.begin(), rows.end(), rows_.get());
+  std::copy(cols.begin(), cols.end(), cols_.get());
+  std::copy(values.begin(), values.end(), values_.get());
+}
+
 TripletSparseMatrix::TripletSparseMatrix(const TripletSparseMatrix& orig)
     : SparseMatrix(),
       num_rows_(orig.num_rows_),
@@ -260,5 +282,37 @@
   }
 }
 
+TripletSparseMatrix* TripletSparseMatrix::CreateRandomMatrix(
+    const TripletSparseMatrix::RandomMatrixOptions& options) {
+  CHECK_GT(options.num_rows, 0);
+  CHECK_GT(options.num_cols, 0);
+  CHECK_GT(options.density, 0.0);
+  CHECK_LE(options.density, 1.0);
+
+  std::vector<int> rows;
+  std::vector<int> cols;
+  std::vector<double> values;
+  while (rows.empty()) {
+    rows.clear();
+    cols.clear();
+    values.clear();
+    for (int r = 0; r < options.num_rows; ++r) {
+      for (int c = 0; c < options.num_cols; ++c) {
+        if (RandDouble() <= options.density) {
+          rows.push_back(r);
+          cols.push_back(c);
+          values.push_back(RandNormal());
+        }
+      }
+    }
+    if (!rows.empty()) {
+      break;
+    }
+  }
+
+  return new TripletSparseMatrix(
+      options.num_rows, options.num_cols, rows, cols, values);
+}
+
 }  // namespace internal
 }  // namespace ceres
diff --git a/internal/ceres/triplet_sparse_matrix.h b/internal/ceres/triplet_sparse_matrix.h
index f3f5370..1d82e14 100644
--- a/internal/ceres/triplet_sparse_matrix.h
+++ b/internal/ceres/triplet_sparse_matrix.h
@@ -31,6 +31,7 @@
 #ifndef CERES_INTERNAL_TRIPLET_SPARSE_MATRIX_H_
 #define CERES_INTERNAL_TRIPLET_SPARSE_MATRIX_H_
 
+#include <vector>
 #include "ceres/sparse_matrix.h"
 #include "ceres/internal/eigen.h"
 #include "ceres/internal/scoped_ptr.h"
@@ -47,6 +48,12 @@
  public:
   TripletSparseMatrix();
   TripletSparseMatrix(int num_rows, int num_cols, int max_num_nonzeros);
+  TripletSparseMatrix(int num_rows,
+                      int num_cols,
+                      const std::vector<int>& rows,
+                      const std::vector<int>& cols,
+                      const std::vector<double>& values);
+
   explicit TripletSparseMatrix(const TripletSparseMatrix& orig);
 
   TripletSparseMatrix& operator=(const TripletSparseMatrix& rhs);
@@ -105,6 +112,25 @@
   static TripletSparseMatrix* CreateSparseDiagonalMatrix(const double* values,
                                                          int num_rows);
 
+  // Options struct to control the generation of random
+  // TripletSparseMatrix objects.
+  struct RandomMatrixOptions {
+    int num_rows;
+    int num_cols;
+    // 0 < density <= 1 is the probability of an entry being
+    // structurally non-zero. A given random matrix will not have
+    // precisely this density.
+    double density;
+  };
+
+  // Create a random CompressedRowSparseMatrix whose entries are
+  // normally distributed and whose structure is determined by
+  // RandomMatrixOptions.
+  //
+  // Caller owns the result.
+  static TripletSparseMatrix* CreateRandomMatrix(
+      const TripletSparseMatrix::RandomMatrixOptions& options);
+
  private:
   void AllocateMemory();
   void CopyData(const TripletSparseMatrix& orig);
diff --git a/internal/ceres/unsymmetric_linear_solver_test.cc b/internal/ceres/unsymmetric_linear_solver_test.cc
index 95797c5..a670f00 100644
--- a/internal/ceres/unsymmetric_linear_solver_test.cc
+++ b/internal/ceres/unsymmetric_linear_solver_test.cc
@@ -69,7 +69,8 @@
         options.type == DENSE_NORMAL_CHOLESKY) {
       transformed_A.reset(new DenseSparseMatrix(*A_));
     } else if (options.type == SPARSE_NORMAL_CHOLESKY) {
-      CompressedRowSparseMatrix* crsm =  new CompressedRowSparseMatrix(*A_);
+      CompressedRowSparseMatrix* crsm =
+          CompressedRowSparseMatrix::FromTripletSparseMatrix(*A_);
       // Add row/column blocks structure.
       for (int i = 0; i < A_->num_rows(); ++i) {
         crsm->mutable_row_blocks()->push_back(1);