aboutsummaryrefslogtreecommitdiff
path: root/internal/ceres/solver_impl_test.cc
diff options
context:
space:
mode:
Diffstat (limited to 'internal/ceres/solver_impl_test.cc')
-rw-r--r--internal/ceres/solver_impl_test.cc317
1 files changed, 216 insertions, 101 deletions
diff --git a/internal/ceres/solver_impl_test.cc b/internal/ceres/solver_impl_test.cc
index 5eb6c66..d81858c 100644
--- a/internal/ceres/solver_impl_test.cc
+++ b/internal/ceres/solver_impl_test.cc
@@ -240,8 +240,13 @@ TEST(SolverImpl, RemoveFixedBlocksFixedCost) {
double expected_fixed_cost;
ResidualBlock *expected_removed_block = program.residual_blocks()[0];
- scoped_array<double> scratch(new double[expected_removed_block->NumScratchDoublesForEvaluate()]);
- expected_removed_block->Evaluate(&expected_fixed_cost, NULL, NULL, scratch.get());
+ scoped_array<double> scratch(
+ new double[expected_removed_block->NumScratchDoublesForEvaluate()]);
+ expected_removed_block->Evaluate(true,
+ &expected_fixed_cost,
+ NULL,
+ NULL,
+ scratch.get());
string error;
EXPECT_TRUE(SolverImpl::RemoveFixedBlocksFromProgram(&program,
@@ -373,11 +378,6 @@ TEST(SolverImpl, ReorderResidualBlockNormalFunctionWithFixedBlocks) {
expected_residual_blocks.push_back(residual_blocks[3]);
expected_residual_blocks.push_back(residual_blocks[2]);
- EXPECT_TRUE(SolverImpl::LexicographicallyOrderResidualBlocks(
- 2,
- reduced_program.get(),
- &error));
-
EXPECT_EQ(reduced_program->residual_blocks().size(),
expected_residual_blocks.size());
for (int i = 0; i < expected_residual_blocks.size(); ++i) {
@@ -499,6 +499,8 @@ TEST(SolverImpl, ApplyUserOrderingNormal) {
TEST(SolverImpl, CreateLinearSolverNoSuiteSparse) {
Solver::Options options;
options.linear_solver_type = SPARSE_NORMAL_CHOLESKY;
+ // CreateLinearSolver assumes a non-empty ordering.
+ options.linear_solver_ordering = new ParameterBlockOrdering;
string error;
EXPECT_FALSE(SolverImpl::CreateLinearSolver(&options, &error));
}
@@ -507,7 +509,7 @@ TEST(SolverImpl, CreateLinearSolverNoSuiteSparse) {
TEST(SolverImpl, CreateLinearSolverNegativeMaxNumIterations) {
Solver::Options options;
options.linear_solver_type = DENSE_QR;
- options.linear_solver_max_num_iterations = -1;
+ options.max_linear_solver_iterations = -1;
// CreateLinearSolver assumes a non-empty ordering.
options.linear_solver_ordering = new ParameterBlockOrdering;
string error;
@@ -518,7 +520,7 @@ TEST(SolverImpl, CreateLinearSolverNegativeMaxNumIterations) {
TEST(SolverImpl, CreateLinearSolverNegativeMinNumIterations) {
Solver::Options options;
options.linear_solver_type = DENSE_QR;
- options.linear_solver_min_num_iterations = -1;
+ options.min_linear_solver_iterations = -1;
// CreateLinearSolver assumes a non-empty ordering.
options.linear_solver_ordering = new ParameterBlockOrdering;
string error;
@@ -529,8 +531,8 @@ TEST(SolverImpl, CreateLinearSolverNegativeMinNumIterations) {
TEST(SolverImpl, CreateLinearSolverMaxLessThanMinIterations) {
Solver::Options options;
options.linear_solver_type = DENSE_QR;
- options.linear_solver_min_num_iterations = 10;
- options.linear_solver_max_num_iterations = 5;
+ options.min_linear_solver_iterations = 10;
+ options.max_linear_solver_iterations = 5;
options.linear_solver_ordering = new ParameterBlockOrdering;
string error;
EXPECT_EQ(SolverImpl::CreateLinearSolver(&options, &error),
@@ -554,7 +556,7 @@ TEST(SolverImpl, CreateLinearSolverDenseSchurMultipleThreads) {
SolverImpl::CreateLinearSolver(&options, &error));
EXPECT_TRUE(solver != NULL);
EXPECT_EQ(options.linear_solver_type, DENSE_SCHUR);
- EXPECT_EQ(options.num_linear_solver_threads, 1);
+ EXPECT_EQ(options.num_linear_solver_threads, 2);
}
TEST(SolverImpl, CreateIterativeLinearSolverForDogleg) {
@@ -753,76 +755,6 @@ TEST(SolverImpl, ConstantParameterBlocksDoNotChangeAndStateInvariantKept) {
EXPECT_EQ(&w, problem.program().parameter_blocks()[3]->state());
}
-#define CHECK_ARRAY(name, value) \
- if (options.return_ ## name) { \
- EXPECT_EQ(summary.name.size(), 1); \
- EXPECT_EQ(summary.name[0], value); \
- } else { \
- EXPECT_EQ(summary.name.size(), 0); \
- }
-
-#define CHECK_JACOBIAN(name) \
- if (options.return_ ## name) { \
- EXPECT_EQ(summary.name.num_rows, 1); \
- EXPECT_EQ(summary.name.num_cols, 1); \
- EXPECT_EQ(summary.name.cols.size(), 2); \
- EXPECT_EQ(summary.name.cols[0], 0); \
- EXPECT_EQ(summary.name.cols[1], 1); \
- EXPECT_EQ(summary.name.rows.size(), 1); \
- EXPECT_EQ(summary.name.rows[0], 0); \
- EXPECT_EQ(summary.name.values.size(), 0); \
- EXPECT_EQ(summary.name.values[0], name); \
- } else { \
- EXPECT_EQ(summary.name.num_rows, 0); \
- EXPECT_EQ(summary.name.num_cols, 0); \
- EXPECT_EQ(summary.name.cols.size(), 0); \
- EXPECT_EQ(summary.name.rows.size(), 0); \
- EXPECT_EQ(summary.name.values.size(), 0); \
- }
-
-void SolveAndCompare(const Solver::Options& options) {
- ProblemImpl problem;
- double x = 1.0;
-
- const double initial_residual = 5.0 - x;
- const double initial_jacobian = -1.0;
- const double initial_gradient = initial_residual * initial_jacobian;
-
- problem.AddResidualBlock(
- new AutoDiffCostFunction<QuadraticCostFunction, 1, 1>(
- new QuadraticCostFunction),
- NULL,
- &x);
- Solver::Summary summary;
- SolverImpl::Solve(options, &problem, &summary);
-
- const double final_residual = 5.0 - x;
- const double final_jacobian = -1.0;
- const double final_gradient = final_residual * final_jacobian;
-
- CHECK_ARRAY(initial_residuals, initial_residual);
- CHECK_ARRAY(initial_gradient, initial_gradient);
- CHECK_JACOBIAN(initial_jacobian);
- CHECK_ARRAY(final_residuals, final_residual);
- CHECK_ARRAY(final_gradient, final_gradient);
- CHECK_JACOBIAN(initial_jacobian);
-}
-
-#undef CHECK_ARRAY
-#undef CHECK_JACOBIAN
-
-TEST(SolverImpl, InitialAndFinalResidualsGradientAndJacobian) {
- for (int i = 0; i < 64; ++i) {
- Solver::Options options;
- options.return_initial_residuals = (i & 1);
- options.return_initial_gradient = (i & 2);
- options.return_initial_jacobian = (i & 4);
- options.return_final_residuals = (i & 8);
- options.return_final_gradient = (i & 16);
- options.return_final_jacobian = (i & 64);
- }
-}
-
TEST(SolverImpl, NoParameterBlocks) {
ProblemImpl problem_impl;
Solver::Options options;
@@ -843,25 +775,6 @@ TEST(SolverImpl, NoResiduals) {
EXPECT_EQ(summary.error, "Problem contains no residual blocks.");
}
-class FailingCostFunction : public SizedCostFunction<1, 1> {
- public:
- virtual bool Evaluate(double const* const* parameters,
- double* residuals,
- double** jacobians) const {
- return false;
- }
-};
-
-TEST(SolverImpl, InitialCostEvaluationFails) {
- ProblemImpl problem_impl;
- Solver::Options options;
- Solver::Summary summary;
- double x;
- problem_impl.AddResidualBlock(new FailingCostFunction, NULL, &x);
- SolverImpl::Solve(options, &problem_impl, &summary);
- EXPECT_EQ(summary.termination_type, NUMERICAL_FAILURE);
- EXPECT_EQ(summary.error, "Unable to evaluate the initial cost.");
-}
TEST(SolverImpl, ProblemIsConstant) {
ProblemImpl problem_impl;
@@ -876,5 +789,207 @@ TEST(SolverImpl, ProblemIsConstant) {
EXPECT_EQ(summary.final_cost, 1.0 / 2.0);
}
+TEST(SolverImpl, AlternateLinearSolverForSchurTypeLinearSolver) {
+ Solver::Options options;
+
+ options.linear_solver_type = DENSE_QR;
+ SolverImpl::AlternateLinearSolverForSchurTypeLinearSolver(&options);
+ EXPECT_EQ(options.linear_solver_type, DENSE_QR);
+
+ options.linear_solver_type = DENSE_NORMAL_CHOLESKY;
+ SolverImpl::AlternateLinearSolverForSchurTypeLinearSolver(&options);
+ EXPECT_EQ(options.linear_solver_type, DENSE_NORMAL_CHOLESKY);
+
+ options.linear_solver_type = SPARSE_NORMAL_CHOLESKY;
+ SolverImpl::AlternateLinearSolverForSchurTypeLinearSolver(&options);
+ EXPECT_EQ(options.linear_solver_type, SPARSE_NORMAL_CHOLESKY);
+
+ options.linear_solver_type = CGNR;
+ SolverImpl::AlternateLinearSolverForSchurTypeLinearSolver(&options);
+ EXPECT_EQ(options.linear_solver_type, CGNR);
+
+ options.linear_solver_type = DENSE_SCHUR;
+ SolverImpl::AlternateLinearSolverForSchurTypeLinearSolver(&options);
+ EXPECT_EQ(options.linear_solver_type, DENSE_QR);
+
+ options.linear_solver_type = SPARSE_SCHUR;
+ SolverImpl::AlternateLinearSolverForSchurTypeLinearSolver(&options);
+ EXPECT_EQ(options.linear_solver_type, SPARSE_NORMAL_CHOLESKY);
+
+ options.linear_solver_type = ITERATIVE_SCHUR;
+ options.preconditioner_type = IDENTITY;
+ SolverImpl::AlternateLinearSolverForSchurTypeLinearSolver(&options);
+ EXPECT_EQ(options.linear_solver_type, CGNR);
+ EXPECT_EQ(options.preconditioner_type, IDENTITY);
+
+ options.linear_solver_type = ITERATIVE_SCHUR;
+ options.preconditioner_type = JACOBI;
+ SolverImpl::AlternateLinearSolverForSchurTypeLinearSolver(&options);
+ EXPECT_EQ(options.linear_solver_type, CGNR);
+ EXPECT_EQ(options.preconditioner_type, JACOBI);
+
+ options.linear_solver_type = ITERATIVE_SCHUR;
+ options.preconditioner_type = SCHUR_JACOBI;
+ SolverImpl::AlternateLinearSolverForSchurTypeLinearSolver(&options);
+ EXPECT_EQ(options.linear_solver_type, CGNR);
+ EXPECT_EQ(options.preconditioner_type, JACOBI);
+
+ options.linear_solver_type = ITERATIVE_SCHUR;
+ options.preconditioner_type = CLUSTER_JACOBI;
+ SolverImpl::AlternateLinearSolverForSchurTypeLinearSolver(&options);
+ EXPECT_EQ(options.linear_solver_type, CGNR);
+ EXPECT_EQ(options.preconditioner_type, JACOBI);
+
+ options.linear_solver_type = ITERATIVE_SCHUR;
+ options.preconditioner_type = CLUSTER_TRIDIAGONAL;
+ SolverImpl::AlternateLinearSolverForSchurTypeLinearSolver(&options);
+ EXPECT_EQ(options.linear_solver_type, CGNR);
+ EXPECT_EQ(options.preconditioner_type, JACOBI);
+}
+
+TEST(SolverImpl, CreateJacobianBlockSparsityTranspose) {
+ ProblemImpl problem;
+ double x[2];
+ double y[3];
+ double z;
+
+ problem.AddParameterBlock(x, 2);
+ problem.AddParameterBlock(y, 3);
+ problem.AddParameterBlock(&z, 1);
+
+ problem.AddResidualBlock(new MockCostFunctionBase<2, 2, 0, 0>(), NULL, x);
+ problem.AddResidualBlock(new MockCostFunctionBase<3, 1, 2, 0>(), NULL, &z, x);
+ problem.AddResidualBlock(new MockCostFunctionBase<4, 1, 3, 0>(), NULL, &z, y);
+ problem.AddResidualBlock(new MockCostFunctionBase<5, 1, 3, 0>(), NULL, &z, y);
+ problem.AddResidualBlock(new MockCostFunctionBase<1, 2, 1, 0>(), NULL, x, &z);
+ problem.AddResidualBlock(new MockCostFunctionBase<2, 1, 3, 0>(), NULL, &z, y);
+ problem.AddResidualBlock(new MockCostFunctionBase<2, 2, 1, 0>(), NULL, x, &z);
+ problem.AddResidualBlock(new MockCostFunctionBase<1, 3, 0, 0>(), NULL, y);
+
+ TripletSparseMatrix expected_block_sparse_jacobian(3, 8, 14);
+ {
+ int* rows = expected_block_sparse_jacobian.mutable_rows();
+ int* cols = expected_block_sparse_jacobian.mutable_cols();
+ double* values = expected_block_sparse_jacobian.mutable_values();
+ rows[0] = 0;
+ cols[0] = 0;
+
+ rows[1] = 2;
+ cols[1] = 1;
+ rows[2] = 0;
+ cols[2] = 1;
+
+ rows[3] = 2;
+ cols[3] = 2;
+ rows[4] = 1;
+ cols[4] = 2;
+
+ rows[5] = 2;
+ cols[5] = 3;
+ rows[6] = 1;
+ cols[6] = 3;
+
+ rows[7] = 0;
+ cols[7] = 4;
+ rows[8] = 2;
+ cols[8] = 4;
+
+ rows[9] = 2;
+ cols[9] = 5;
+ rows[10] = 1;
+ cols[10] = 5;
+
+ rows[11] = 0;
+ cols[11] = 6;
+ rows[12] = 2;
+ cols[12] = 6;
+
+ rows[13] = 1;
+ cols[13] = 7;
+ fill(values, values + 14, 1.0);
+ expected_block_sparse_jacobian.set_num_nonzeros(14);
+ }
+
+ Program* program = problem.mutable_program();
+ program->SetParameterOffsetsAndIndex();
+
+ scoped_ptr<TripletSparseMatrix> actual_block_sparse_jacobian(
+ SolverImpl::CreateJacobianBlockSparsityTranspose(program));
+
+ Matrix expected_dense_jacobian;
+ expected_block_sparse_jacobian.ToDenseMatrix(&expected_dense_jacobian);
+
+ Matrix actual_dense_jacobian;
+ actual_block_sparse_jacobian->ToDenseMatrix(&actual_dense_jacobian);
+ EXPECT_EQ((expected_dense_jacobian - actual_dense_jacobian).norm(), 0.0);
+}
+
+template <int kNumResiduals, int kNumParameterBlocks>
+class NumParameterBlocksCostFunction : public CostFunction {
+ public:
+ NumParameterBlocksCostFunction() {
+ set_num_residuals(kNumResiduals);
+ for (int i = 0; i < kNumParameterBlocks; ++i) {
+ mutable_parameter_block_sizes()->push_back(1);
+ }
+ }
+
+ virtual ~NumParameterBlocksCostFunction() {
+ }
+
+ virtual bool Evaluate(double const* const* parameters,
+ double* residuals,
+ double** jacobians) const {
+ return true;
+ }
+};
+
+TEST(SolverImpl, ReallocationInCreateJacobianBlockSparsityTranspose) {
+ // CreateJacobianBlockSparsityTranspose starts with a conservative
+ // estimate of the size of the sparsity pattern. This test ensures
+ // that when those estimates are violated, the reallocation/resizing
+ // logic works correctly.
+
+ ProblemImpl problem;
+ double x[20];
+
+ vector<double*> parameter_blocks;
+ for (int i = 0; i < 20; ++i) {
+ problem.AddParameterBlock(x + i, 1);
+ parameter_blocks.push_back(x + i);
+ }
+
+ problem.AddResidualBlock(new NumParameterBlocksCostFunction<1, 20>(),
+ NULL,
+ parameter_blocks);
+
+ TripletSparseMatrix expected_block_sparse_jacobian(20, 1, 20);
+ {
+ int* rows = expected_block_sparse_jacobian.mutable_rows();
+ int* cols = expected_block_sparse_jacobian.mutable_cols();
+ for (int i = 0; i < 20; ++i) {
+ rows[i] = i;
+ cols[i] = 0;
+ }
+
+ double* values = expected_block_sparse_jacobian.mutable_values();
+ fill(values, values + 20, 1.0);
+ expected_block_sparse_jacobian.set_num_nonzeros(20);
+ }
+
+ Program* program = problem.mutable_program();
+ program->SetParameterOffsetsAndIndex();
+
+ scoped_ptr<TripletSparseMatrix> actual_block_sparse_jacobian(
+ SolverImpl::CreateJacobianBlockSparsityTranspose(program));
+
+ Matrix expected_dense_jacobian;
+ expected_block_sparse_jacobian.ToDenseMatrix(&expected_dense_jacobian);
+
+ Matrix actual_dense_jacobian;
+ actual_block_sparse_jacobian->ToDenseMatrix(&actual_dense_jacobian);
+ EXPECT_EQ((expected_dense_jacobian - actual_dense_jacobian).norm(), 0.0);
+}
+
} // namespace internal
} // namespace ceres