Optimize J' * J in sparse_normal_cholesky_solver.

1. Add stype to the outerproduct computation to control the output
matrix in upper or lower triangular matrix. For SuiteSparse,
upper triangular matrix is generated. SuiteSparse can directly use
this matrix format for cholesky without matrix transpose overhead.

2. Change the outerproduct computation to block multiplication.  This
reduces the computation complexity for the sort in preprocessing, also
allows formulation of the block outerproduct computation as dense Eigen
block matrix multiplication.

3. Solve 32 Tango problems on Qualcomm MSM8994 Cortex-A53 (1.55GHz)
   before change: 140 seconds
   after change: 131 seconds

Change-Id: I8054114cef911de6a303310a448821ca296e4744
This commit is contained in:
Cheng Wang
2017-03-07 14:57:23 -08:00
parent 0a50cd8244
commit 07dbf31eca
8 changed files with 515 additions and 100 deletions
@@ -89,6 +89,13 @@ class CompressedRowSparseMatrixTest : public ::testing::Test {
vector<int>* col_blocks = crsm->mutable_col_blocks();
col_blocks->resize(num_cols);
std::fill(col_blocks->begin(), col_blocks->end(), 1);
// 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::back_inserter(*crsm->mutable_crsb_rows()));
std::copy(crsm->cols(), crsm->cols() + crsm->num_nonzeros(),
std::back_inserter(*crsm->mutable_crsb_cols()));
}
int num_rows;
@@ -142,6 +149,9 @@ TEST_F(CompressedRowSparseMatrixTest, DeleteRows) {
// Clear the row and column blocks as these are purely scalar tests.
crsm->mutable_row_blocks()->clear();
crsm->mutable_col_blocks()->clear();
crsm->mutable_crsb_rows()->clear();
crsm->mutable_crsb_cols()->clear();
for (int i = 0; i < num_rows; ++i) {
tsm->Resize(num_rows - i, num_cols);
crsm->DeleteRows(crsm->num_rows() - tsm->num_rows());
@@ -153,6 +163,8 @@ TEST_F(CompressedRowSparseMatrixTest, AppendRows) {
// Clear the row and column blocks as these are purely scalar tests.
crsm->mutable_row_blocks()->clear();
crsm->mutable_col_blocks()->clear();
crsm->mutable_crsb_rows()->clear();
crsm->mutable_crsb_cols()->clear();
for (int i = 0; i < num_rows; ++i) {
TripletSparseMatrix tsm_appendage(*tsm);
@@ -182,6 +194,9 @@ TEST_F(CompressedRowSparseMatrixTest, AppendAndDeleteBlockDiagonalMatrix) {
const vector<int> pre_row_blocks = crsm->row_blocks();
const vector<int> pre_col_blocks = crsm->col_blocks();
const vector<int> pre_crsb_rows = crsm->crsb_rows();
const vector<int> pre_crsb_cols = crsm->crsb_cols();
scoped_ptr<CompressedRowSparseMatrix> appendage(
CompressedRowSparseMatrix::CreateBlockDiagonalMatrix(
diagonal.get(), row_and_column_blocks));
@@ -202,9 +217,23 @@ TEST_F(CompressedRowSparseMatrixTest, AppendAndDeleteBlockDiagonalMatrix) {
EXPECT_EQ(expected_row_blocks, crsm->row_blocks());
EXPECT_EQ(expected_col_blocks, crsm->col_blocks());
EXPECT_EQ(crsm->crsb_cols().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());
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);
EXPECT_EQ(crsm->crsb_cols()[i + pre_crsb_cols.size()], i);
}
crsm->DeleteRows(num_diagonal_rows);
EXPECT_EQ(crsm->row_blocks(), pre_row_blocks);
EXPECT_EQ(crsm->col_blocks(), pre_col_blocks);
EXPECT_EQ(crsm->crsb_rows(), pre_crsb_rows);
EXPECT_EQ(crsm->crsb_cols(), pre_crsb_cols);
}
TEST_F(CompressedRowSparseMatrixTest, ToDenseMatrix) {
@@ -357,6 +386,14 @@ TEST(CompressedRowSparseMatrix, Transpose) {
matrix.mutable_col_blocks()->push_back(4);
matrix.mutable_col_blocks()->push_back(2);
matrix.mutable_crsb_rows()->push_back(0);
matrix.mutable_crsb_rows()->push_back(2);
matrix.mutable_crsb_rows()->push_back(4);
matrix.mutable_crsb_cols()->push_back(0);
matrix.mutable_crsb_cols()->push_back(1);
matrix.mutable_crsb_cols()->push_back(0);
matrix.mutable_crsb_cols()->push_back(1);
rows[0] = 0;
cols[0] = 1;
cols[1] = 3;
@@ -440,10 +477,16 @@ CompressedRowSparseMatrix* CreateRandomCompressedRowSparseMatrix(
vector<int> cols;
vector<double> values;
vector<int> crsb_rows;
vector<int> crsb_cols;
while (values.size() == 0) {
int row_block_begin = 0;
crsb_rows.clear();
crsb_cols.clear();
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) {
if (RandDouble() <= options.block_density) {
for (int i = 0; i < row_blocks[r]; ++i) {
@@ -453,11 +496,13 @@ CompressedRowSparseMatrix* CreateRandomCompressedRowSparseMatrix(
values.push_back(RandNormal());
}
}
crsb_cols.push_back(c);
}
col_block_begin += col_blocks[c];
}
row_block_begin += row_blocks[r];
}
crsb_rows.push_back(crsb_cols.size());
}
const int num_rows = std::accumulate(row_blocks.begin(), row_blocks.end(), 0);
@@ -472,6 +517,8 @@ CompressedRowSparseMatrix* CreateRandomCompressedRowSparseMatrix(
CompressedRowSparseMatrix* matrix = new CompressedRowSparseMatrix(tsm);
(*matrix->mutable_row_blocks()) = row_blocks;
(*matrix->mutable_col_blocks()) = col_blocks;
(*matrix->mutable_crsb_rows()) = crsb_rows;
(*matrix->mutable_crsb_cols()) = crsb_cols;
return matrix;
}
@@ -534,11 +581,14 @@ TEST(CompressedRowSparseMatrix, ComputeOuterProduct) {
cs_di* expected_outer_product =
cxsparse.MatrixMatrixMultiply(&cs_matrix_transpose, cs_matrix);
// Use compressed row lower triangular matrix for cxsparse.
const int stype = 1;
vector<int> program;
scoped_ptr<CompressedRowSparseMatrix> outer_product(
CompressedRowSparseMatrix::CreateOuterProductMatrixAndProgram(
*matrix, &program));
*matrix, stype, &program));
CompressedRowSparseMatrix::ComputeOuterProduct(*matrix,
stype,
program,
outer_product.get());
@@ -556,6 +606,7 @@ TEST(CompressedRowSparseMatrix, ComputeOuterProduct) {
expected_matrix.triangularView<Eigen::StrictlyLower>().setZero();
ToDenseMatrix(&actual_outer_product, &actual_matrix);
actual_matrix.triangularView<Eigen::StrictlyLower>().setZero();
const double diff_norm =
(actual_matrix - expected_matrix).norm() / expected_matrix.norm();
ASSERT_NEAR(diff_norm, 0.0, kTolerance)