blob: 23d36747cecec003dc7d2b4daa3aa1727149dff3 [file] [log] [blame]
Austin Schuh70cc9552019-01-21 19:46:48 -08001// Ceres Solver - A fast non-linear least squares minimizer
2// Copyright 2015 Google Inc. All rights reserved.
3// http://ceres-solver.org/
4//
5// Redistribution and use in source and binary forms, with or without
6// modification, are permitted provided that the following conditions are met:
7//
8// * Redistributions of source code must retain the above copyright notice,
9// this list of conditions and the following disclaimer.
10// * Redistributions in binary form must reproduce the above copyright notice,
11// this list of conditions and the following disclaimer in the documentation
12// and/or other materials provided with the distribution.
13// * Neither the name of Google Inc. nor the names of its contributors may be
14// used to endorse or promote products derived from this software without
15// specific prior written permission.
16//
17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27// POSSIBILITY OF SUCH DAMAGE.
28//
29// Author: sameeragarwal@google.com (Sameer Agarwal)
30
31#include "ceres/schur_complement_solver.h"
32
33#include <cstddef>
34#include <memory>
35
36#include "ceres/block_sparse_matrix.h"
37#include "ceres/block_structure.h"
38#include "ceres/casts.h"
39#include "ceres/context_impl.h"
40#include "ceres/detect_structure.h"
41#include "ceres/linear_least_squares_problems.h"
42#include "ceres/linear_solver.h"
43#include "ceres/triplet_sparse_matrix.h"
44#include "ceres/types.h"
45#include "glog/logging.h"
46#include "gtest/gtest.h"
47
48namespace ceres {
49namespace internal {
50
51class SchurComplementSolverTest : public ::testing::Test {
52 protected:
53 void SetUpFromProblemId(int problem_id) {
54 std::unique_ptr<LinearLeastSquaresProblem> problem(
55 CreateLinearLeastSquaresProblemFromId(problem_id));
56
57 CHECK(problem != nullptr);
58 A.reset(down_cast<BlockSparseMatrix*>(problem->A.release()));
59 b.reset(problem->b.release());
60 D.reset(problem->D.release());
61
62 num_cols = A->num_cols();
63 num_rows = A->num_rows();
64 num_eliminate_blocks = problem->num_eliminate_blocks;
65
66 x.resize(num_cols);
67 sol.resize(num_cols);
68 sol_d.resize(num_cols);
69
70 LinearSolver::Options options;
71 options.type = DENSE_QR;
72 ContextImpl context;
73 options.context = &context;
74
75 std::unique_ptr<LinearSolver> qr(LinearSolver::Create(options));
76
77 TripletSparseMatrix triplet_A(A->num_rows(),
78 A->num_cols(),
79 A->num_nonzeros());
80 A->ToTripletSparseMatrix(&triplet_A);
81
82 // Gold standard solutions using dense QR factorization.
83 DenseSparseMatrix dense_A(triplet_A);
84 qr->Solve(&dense_A, b.get(), LinearSolver::PerSolveOptions(), sol.data());
85
86 // Gold standard solution with appended diagonal.
87 LinearSolver::PerSolveOptions per_solve_options;
88 per_solve_options.D = D.get();
89 qr->Solve(&dense_A, b.get(), per_solve_options, sol_d.data());
90 }
91
92 void ComputeAndCompareSolutions(
93 int problem_id,
94 bool regularization,
95 ceres::LinearSolverType linear_solver_type,
96 ceres::DenseLinearAlgebraLibraryType dense_linear_algebra_library_type,
97 ceres::SparseLinearAlgebraLibraryType sparse_linear_algebra_library_type,
98 bool use_postordering) {
99 SetUpFromProblemId(problem_id);
100 LinearSolver::Options options;
101 options.elimination_groups.push_back(num_eliminate_blocks);
102 options.elimination_groups.push_back(
103 A->block_structure()->cols.size() - num_eliminate_blocks);
104 options.type = linear_solver_type;
105 options.dense_linear_algebra_library_type =
106 dense_linear_algebra_library_type;
107 options.sparse_linear_algebra_library_type =
108 sparse_linear_algebra_library_type;
109 options.use_postordering = use_postordering;
110 ContextImpl context;
111 options.context = &context;
112 DetectStructure(*A->block_structure(),
113 num_eliminate_blocks,
114 &options.row_block_size,
115 &options.e_block_size,
116 &options.f_block_size);
117
118 std::unique_ptr<LinearSolver> solver(LinearSolver::Create(options));
119
120 LinearSolver::PerSolveOptions per_solve_options;
121 LinearSolver::Summary summary;
122 if (regularization) {
123 per_solve_options.D = D.get();
124 }
125
126 summary = solver->Solve(A.get(), b.get(), per_solve_options, x.data());
127 EXPECT_EQ(summary.termination_type, LINEAR_SOLVER_SUCCESS);
128
129 if (regularization) {
130
131 ASSERT_NEAR((sol_d - x).norm() / num_cols, 0, 1e-10)
132 << "Regularized Expected solution: " << sol_d.transpose()
133 << " Actual solution: " << x.transpose();
134 } else {
135 ASSERT_NEAR((sol - x).norm() / num_cols, 0, 1e-10)
136 << "Unregularized Expected solution: " << sol.transpose()
137 << " Actual solution: " << x.transpose();
138 }
139 }
140
141 int num_rows;
142 int num_cols;
143 int num_eliminate_blocks;
144
145 std::unique_ptr<BlockSparseMatrix> A;
146 std::unique_ptr<double[]> b;
147 std::unique_ptr<double[]> D;
148 Vector x;
149 Vector sol;
150 Vector sol_d;
151};
152
153// TODO(sameeragarwal): Refactor these using value parameterized tests.
154// TODO(sameeragarwal): More extensive tests using random matrices.
155TEST_F(SchurComplementSolverTest, DenseSchurWithEigenSmallProblem) {
156 ComputeAndCompareSolutions(2, false, DENSE_SCHUR, EIGEN, SUITE_SPARSE, true);
157 ComputeAndCompareSolutions(2, true, DENSE_SCHUR, EIGEN, SUITE_SPARSE, true);
158}
159
160TEST_F(SchurComplementSolverTest, DenseSchurWithEigenLargeProblem) {
161 ComputeAndCompareSolutions(3, false, DENSE_SCHUR, EIGEN, SUITE_SPARSE, true);
162 ComputeAndCompareSolutions(3, true, DENSE_SCHUR, EIGEN, SUITE_SPARSE, true);
163}
164
165TEST_F(SchurComplementSolverTest, DenseSchurWithEigenVaryingFBlockSize) {
166 ComputeAndCompareSolutions(4, true, DENSE_SCHUR, EIGEN, SUITE_SPARSE, true);
167}
168
169#ifndef CERES_NO_LAPACK
170TEST_F(SchurComplementSolverTest, DenseSchurWithLAPACKSmallProblem) {
171 ComputeAndCompareSolutions(2, false, DENSE_SCHUR, LAPACK, SUITE_SPARSE, true);
172 ComputeAndCompareSolutions(2, true, DENSE_SCHUR, LAPACK, SUITE_SPARSE, true);
173}
174
175TEST_F(SchurComplementSolverTest, DenseSchurWithLAPACKLargeProblem) {
176 ComputeAndCompareSolutions(3, false, DENSE_SCHUR, LAPACK, SUITE_SPARSE, true);
177 ComputeAndCompareSolutions(3, true, DENSE_SCHUR, LAPACK, SUITE_SPARSE, true);
178}
179#endif
180
181#ifndef CERES_NO_SUITESPARSE
182TEST_F(SchurComplementSolverTest,
183 SparseSchurWithSuiteSparseSmallProblemNoPostOrdering) {
184 ComputeAndCompareSolutions(
185 2, false, SPARSE_SCHUR, EIGEN, SUITE_SPARSE, false);
186 ComputeAndCompareSolutions(2, true, SPARSE_SCHUR, EIGEN, SUITE_SPARSE, false);
187}
188
189TEST_F(SchurComplementSolverTest,
190 SparseSchurWithSuiteSparseSmallProblemPostOrdering) {
191 ComputeAndCompareSolutions(2, false, SPARSE_SCHUR, EIGEN, SUITE_SPARSE, true);
192 ComputeAndCompareSolutions(2, true, SPARSE_SCHUR, EIGEN, SUITE_SPARSE, true);
193}
194
195TEST_F(SchurComplementSolverTest,
196 SparseSchurWithSuiteSparseLargeProblemNoPostOrdering) {
197 ComputeAndCompareSolutions(
198 3, false, SPARSE_SCHUR, EIGEN, SUITE_SPARSE, false);
199 ComputeAndCompareSolutions(3, true, SPARSE_SCHUR, EIGEN, SUITE_SPARSE, false);
200}
201
202TEST_F(SchurComplementSolverTest,
203 SparseSchurWithSuiteSparseLargeProblemPostOrdering) {
204 ComputeAndCompareSolutions(3, false, SPARSE_SCHUR, EIGEN, SUITE_SPARSE, true);
205 ComputeAndCompareSolutions(3, true, SPARSE_SCHUR, EIGEN, SUITE_SPARSE, true);
206}
207#endif // CERES_NO_SUITESPARSE
208
209#ifndef CERES_NO_CXSPARSE
210TEST_F(SchurComplementSolverTest,
211 SparseSchurWithCXSparseSmallProblem) {
212 ComputeAndCompareSolutions(2, false, SPARSE_SCHUR, EIGEN, CX_SPARSE, true);
213 ComputeAndCompareSolutions(2, true, SPARSE_SCHUR, EIGEN, CX_SPARSE, true);
214}
215
216TEST_F(SchurComplementSolverTest,
217 SparseSchurWithCXSparseLargeProblem) {
218 ComputeAndCompareSolutions(3, false, SPARSE_SCHUR, EIGEN, CX_SPARSE, true);
219 ComputeAndCompareSolutions(3, true, SPARSE_SCHUR, EIGEN, CX_SPARSE, true);
220}
221#endif // CERES_NO_CXSPARSE
222
223#ifndef CERES_NO_ACCELERATE_SPARSE
224TEST_F(SchurComplementSolverTest,
225 SparseSchurWithAccelerateSparseSmallProblem) {
226 ComputeAndCompareSolutions(2, false, SPARSE_SCHUR, EIGEN, ACCELERATE_SPARSE, true);
227 ComputeAndCompareSolutions(2, true, SPARSE_SCHUR, EIGEN, ACCELERATE_SPARSE, true);
228}
229
230TEST_F(SchurComplementSolverTest,
231 SparseSchurWithAccelerateSparseLargeProblem) {
232 ComputeAndCompareSolutions(3, false, SPARSE_SCHUR, EIGEN, ACCELERATE_SPARSE, true);
233 ComputeAndCompareSolutions(3, true, SPARSE_SCHUR, EIGEN, ACCELERATE_SPARSE, true);
234}
235#endif // CERES_NO_ACCELERATE_SPARSE
236
237#ifdef CERES_USE_EIGEN_SPARSE
238TEST_F(SchurComplementSolverTest,
239 SparseSchurWithEigenSparseSmallProblem) {
240 ComputeAndCompareSolutions(2, false, SPARSE_SCHUR, EIGEN, EIGEN_SPARSE, true);
241 ComputeAndCompareSolutions(2, true, SPARSE_SCHUR, EIGEN, EIGEN_SPARSE, true);
242}
243
244TEST_F(SchurComplementSolverTest,
245 SparseSchurWithEigenSparseLargeProblem) {
246 ComputeAndCompareSolutions(3, false, SPARSE_SCHUR, EIGEN, EIGEN_SPARSE, true);
247 ComputeAndCompareSolutions(3, true, SPARSE_SCHUR, EIGEN, EIGEN_SPARSE, true);
248}
249#endif // CERES_USE_EIGEN_SPARSE
250
251} // namespace internal
252} // namespace ceres