Fix a bug in InnerProductComputer.
InnerProductComputer was assuming that the input matrix had
at least one structurally non-zero block. As a result sometimes
when InnerProductComputer.SubMatrix test generated matrices
where the submatrix was structurally zero it would cause
a segmentation fault.
This changes fixes this issue and reduces the threshold for the
minimum block density so that there is a much higher chance of
such matrices occuring as part of testing.
Fixes https://github.com/ceres-solver/ceres-solver/issues/820
Change-Id: Iec3a2431e646f0c2aac9e0b994531fa72323f329
diff --git a/internal/ceres/inner_product_computer.cc b/internal/ceres/inner_product_computer.cc
index 69a68ee..a75cbe5 100644
--- a/internal/ceres/inner_product_computer.cc
+++ b/internal/ceres/inner_product_computer.cc
@@ -77,6 +77,10 @@
row_nnz->resize(blocks.size());
std::fill(row_nnz->begin(), row_nnz->end(), 0);
+ if (product_terms.empty()) {
+ return 0;
+ }
+
// First product term.
(*row_nnz)[product_terms[0].row] = blocks[product_terms[0].col].size;
int num_nonzeros =
@@ -197,6 +201,10 @@
*(crsm_rows + 1) = *crsm_rows + row_block_nnz[i];
}
}
+ result_offsets_.resize(product_terms.size());
+ if (num_nonzeros == 0) {
+ return;
+ }
// The following macro FILL_CRSM_COL_BLOCK is key to understanding
// how this class works.
@@ -243,7 +251,6 @@
} \
}
- result_offsets_.resize(product_terms.size());
int col_nnz = 0;
int nnz = 0;
diff --git a/internal/ceres/inner_product_computer_test.cc b/internal/ceres/inner_product_computer_test.cc
index ba29da3..a1f5c49 100644
--- a/internal/ceres/inner_product_computer_test.cc
+++ b/internal/ceres/inner_product_computer_test.cc
@@ -44,36 +44,36 @@
namespace ceres {
namespace internal {
-#define COMPUTE_AND_COMPARE \
- { \
- inner_product_computer->Compute(); \
- CompressedRowSparseMatrix* actual_product_crsm = \
- inner_product_computer->mutable_result(); \
- Matrix actual_inner_product = \
- Eigen::Map<Eigen::SparseMatrix<double, Eigen::ColMajor>>( \
- actual_product_crsm->num_rows(), \
- actual_product_crsm->num_rows(), \
- actual_product_crsm->num_nonzeros(), \
- actual_product_crsm->mutable_rows(), \
- actual_product_crsm->mutable_cols(), \
- actual_product_crsm->mutable_values()); \
- EXPECT_EQ(actual_inner_product.rows(), actual_inner_product.cols()); \
- EXPECT_EQ(expected_inner_product.rows(), expected_inner_product.cols()); \
- EXPECT_EQ(actual_inner_product.rows(), expected_inner_product.rows()); \
- Matrix expected_t, actual_t; \
- if (actual_product_crsm->storage_type() == \
- CompressedRowSparseMatrix::StorageType::LOWER_TRIANGULAR) { \
- expected_t = expected_inner_product.triangularView<Eigen::Upper>(); \
- actual_t = actual_inner_product.triangularView<Eigen::Upper>(); \
- } else { \
- expected_t = expected_inner_product.triangularView<Eigen::Lower>(); \
- actual_t = actual_inner_product.triangularView<Eigen::Lower>(); \
- } \
- EXPECT_LE((expected_t - actual_t).norm() / actual_t.norm(), \
- 100 * std::numeric_limits<double>::epsilon()) \
- << "expected: \n" \
- << expected_t << "\nactual: \n" \
- << actual_t; \
+#define COMPUTE_AND_COMPARE \
+ { \
+ inner_product_computer->Compute(); \
+ CompressedRowSparseMatrix* actual_product_crsm = \
+ inner_product_computer->mutable_result(); \
+ Matrix actual_inner_product = \
+ Eigen::Map<Eigen::SparseMatrix<double, Eigen::ColMajor>>( \
+ actual_product_crsm->num_rows(), \
+ actual_product_crsm->num_rows(), \
+ actual_product_crsm->num_nonzeros(), \
+ actual_product_crsm->mutable_rows(), \
+ actual_product_crsm->mutable_cols(), \
+ actual_product_crsm->mutable_values()); \
+ EXPECT_EQ(actual_inner_product.rows(), actual_inner_product.cols()); \
+ EXPECT_EQ(expected_inner_product.rows(), expected_inner_product.cols()); \
+ EXPECT_EQ(actual_inner_product.rows(), expected_inner_product.rows()); \
+ Matrix expected_t, actual_t; \
+ if (actual_product_crsm->storage_type() == \
+ CompressedRowSparseMatrix::StorageType::LOWER_TRIANGULAR) { \
+ expected_t = expected_inner_product.triangularView<Eigen::Upper>(); \
+ actual_t = actual_inner_product.triangularView<Eigen::Upper>(); \
+ } else { \
+ expected_t = expected_inner_product.triangularView<Eigen::Lower>(); \
+ actual_t = actual_inner_product.triangularView<Eigen::Lower>(); \
+ } \
+ EXPECT_LE((expected_t - actual_t).norm(), \
+ 100 * std::numeric_limits<double>::epsilon() * actual_t.norm()) \
+ << "expected: \n" \
+ << expected_t << "\nactual: \n" \
+ << actual_t; \
}
TEST(InnerProductComputer, NormalOperation) {
@@ -99,7 +99,6 @@
options.min_col_block_size = 1;
options.max_col_block_size = 10;
options.block_density = std::max(0.1, RandDouble());
-
VLOG(2) << "num row blocks: " << options.num_row_blocks;
VLOG(2) << "num col blocks: " << options.num_col_blocks;
VLOG(2) << "min row block size: " << options.min_row_block_size;
@@ -141,10 +140,6 @@
}
TEST(InnerProductComputer, SubMatrix) {
- // "Randomly generated seed."
- // FIXME Use a seed different from the previous one to allow the test to pass
- // without a segmentation fault when compiled using 32 bit MinGW.
- SetRandomState(29824);
const int kNumRowBlocks = 10;
const int kNumColBlocks = 20;
const int kNumTrials = 5;
@@ -160,7 +155,7 @@
options.max_row_block_size = 5;
options.min_col_block_size = 1;
options.max_col_block_size = 10;
- options.block_density = std::max(0.1, RandDouble());
+ options.block_density = std::min(0.01, RandDouble());
VLOG(2) << "num row blocks: " << options.num_row_blocks;
VLOG(2) << "num col blocks: " << options.num_col_blocks;