blob: 550733e52ae567e6dbfd6d30f0b5934f09ea231e [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
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080077 TripletSparseMatrix triplet_A(
78 A->num_rows(), A->num_cols(), A->num_nonzeros());
Austin Schuh70cc9552019-01-21 19:46:48 -080079 A->ToTripletSparseMatrix(&triplet_A);
80
81 // Gold standard solutions using dense QR factorization.
82 DenseSparseMatrix dense_A(triplet_A);
83 qr->Solve(&dense_A, b.get(), LinearSolver::PerSolveOptions(), sol.data());
84
85 // Gold standard solution with appended diagonal.
86 LinearSolver::PerSolveOptions per_solve_options;
87 per_solve_options.D = D.get();
88 qr->Solve(&dense_A, b.get(), per_solve_options, sol_d.data());
89 }
90
91 void ComputeAndCompareSolutions(
92 int problem_id,
93 bool regularization,
94 ceres::LinearSolverType linear_solver_type,
95 ceres::DenseLinearAlgebraLibraryType dense_linear_algebra_library_type,
96 ceres::SparseLinearAlgebraLibraryType sparse_linear_algebra_library_type,
97 bool use_postordering) {
98 SetUpFromProblemId(problem_id);
99 LinearSolver::Options options;
100 options.elimination_groups.push_back(num_eliminate_blocks);
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800101 options.elimination_groups.push_back(A->block_structure()->cols.size() -
102 num_eliminate_blocks);
Austin Schuh70cc9552019-01-21 19:46:48 -0800103 options.type = linear_solver_type;
104 options.dense_linear_algebra_library_type =
105 dense_linear_algebra_library_type;
106 options.sparse_linear_algebra_library_type =
107 sparse_linear_algebra_library_type;
108 options.use_postordering = use_postordering;
109 ContextImpl context;
110 options.context = &context;
111 DetectStructure(*A->block_structure(),
112 num_eliminate_blocks,
113 &options.row_block_size,
114 &options.e_block_size,
115 &options.f_block_size);
116
117 std::unique_ptr<LinearSolver> solver(LinearSolver::Create(options));
118
119 LinearSolver::PerSolveOptions per_solve_options;
120 LinearSolver::Summary summary;
121 if (regularization) {
122 per_solve_options.D = D.get();
123 }
124
125 summary = solver->Solve(A.get(), b.get(), per_solve_options, x.data());
126 EXPECT_EQ(summary.termination_type, LINEAR_SOLVER_SUCCESS);
127
128 if (regularization) {
Austin Schuh70cc9552019-01-21 19:46:48 -0800129 ASSERT_NEAR((sol_d - x).norm() / num_cols, 0, 1e-10)
130 << "Regularized Expected solution: " << sol_d.transpose()
131 << " Actual solution: " << x.transpose();
132 } else {
133 ASSERT_NEAR((sol - x).norm() / num_cols, 0, 1e-10)
134 << "Unregularized Expected solution: " << sol.transpose()
135 << " Actual solution: " << x.transpose();
136 }
137 }
138
139 int num_rows;
140 int num_cols;
141 int num_eliminate_blocks;
142
143 std::unique_ptr<BlockSparseMatrix> A;
144 std::unique_ptr<double[]> b;
145 std::unique_ptr<double[]> D;
146 Vector x;
147 Vector sol;
148 Vector sol_d;
149};
150
151// TODO(sameeragarwal): Refactor these using value parameterized tests.
152// TODO(sameeragarwal): More extensive tests using random matrices.
153TEST_F(SchurComplementSolverTest, DenseSchurWithEigenSmallProblem) {
154 ComputeAndCompareSolutions(2, false, DENSE_SCHUR, EIGEN, SUITE_SPARSE, true);
155 ComputeAndCompareSolutions(2, true, DENSE_SCHUR, EIGEN, SUITE_SPARSE, true);
156}
157
158TEST_F(SchurComplementSolverTest, DenseSchurWithEigenLargeProblem) {
159 ComputeAndCompareSolutions(3, false, DENSE_SCHUR, EIGEN, SUITE_SPARSE, true);
160 ComputeAndCompareSolutions(3, true, DENSE_SCHUR, EIGEN, SUITE_SPARSE, true);
161}
162
163TEST_F(SchurComplementSolverTest, DenseSchurWithEigenVaryingFBlockSize) {
164 ComputeAndCompareSolutions(4, true, DENSE_SCHUR, EIGEN, SUITE_SPARSE, true);
165}
166
167#ifndef CERES_NO_LAPACK
168TEST_F(SchurComplementSolverTest, DenseSchurWithLAPACKSmallProblem) {
169 ComputeAndCompareSolutions(2, false, DENSE_SCHUR, LAPACK, SUITE_SPARSE, true);
170 ComputeAndCompareSolutions(2, true, DENSE_SCHUR, LAPACK, SUITE_SPARSE, true);
171}
172
173TEST_F(SchurComplementSolverTest, DenseSchurWithLAPACKLargeProblem) {
174 ComputeAndCompareSolutions(3, false, DENSE_SCHUR, LAPACK, SUITE_SPARSE, true);
175 ComputeAndCompareSolutions(3, true, DENSE_SCHUR, LAPACK, SUITE_SPARSE, true);
176}
177#endif
178
179#ifndef CERES_NO_SUITESPARSE
180TEST_F(SchurComplementSolverTest,
181 SparseSchurWithSuiteSparseSmallProblemNoPostOrdering) {
182 ComputeAndCompareSolutions(
183 2, false, SPARSE_SCHUR, EIGEN, SUITE_SPARSE, false);
184 ComputeAndCompareSolutions(2, true, SPARSE_SCHUR, EIGEN, SUITE_SPARSE, false);
185}
186
187TEST_F(SchurComplementSolverTest,
188 SparseSchurWithSuiteSparseSmallProblemPostOrdering) {
189 ComputeAndCompareSolutions(2, false, SPARSE_SCHUR, EIGEN, SUITE_SPARSE, true);
190 ComputeAndCompareSolutions(2, true, SPARSE_SCHUR, EIGEN, SUITE_SPARSE, true);
191}
192
193TEST_F(SchurComplementSolverTest,
194 SparseSchurWithSuiteSparseLargeProblemNoPostOrdering) {
195 ComputeAndCompareSolutions(
196 3, false, SPARSE_SCHUR, EIGEN, SUITE_SPARSE, false);
197 ComputeAndCompareSolutions(3, true, SPARSE_SCHUR, EIGEN, SUITE_SPARSE, false);
198}
199
200TEST_F(SchurComplementSolverTest,
201 SparseSchurWithSuiteSparseLargeProblemPostOrdering) {
202 ComputeAndCompareSolutions(3, false, SPARSE_SCHUR, EIGEN, SUITE_SPARSE, true);
203 ComputeAndCompareSolutions(3, true, SPARSE_SCHUR, EIGEN, SUITE_SPARSE, true);
204}
205#endif // CERES_NO_SUITESPARSE
206
207#ifndef CERES_NO_CXSPARSE
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800208TEST_F(SchurComplementSolverTest, SparseSchurWithCXSparseSmallProblem) {
Austin Schuh70cc9552019-01-21 19:46:48 -0800209 ComputeAndCompareSolutions(2, false, SPARSE_SCHUR, EIGEN, CX_SPARSE, true);
210 ComputeAndCompareSolutions(2, true, SPARSE_SCHUR, EIGEN, CX_SPARSE, true);
211}
212
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800213TEST_F(SchurComplementSolverTest, SparseSchurWithCXSparseLargeProblem) {
Austin Schuh70cc9552019-01-21 19:46:48 -0800214 ComputeAndCompareSolutions(3, false, SPARSE_SCHUR, EIGEN, CX_SPARSE, true);
215 ComputeAndCompareSolutions(3, true, SPARSE_SCHUR, EIGEN, CX_SPARSE, true);
216}
217#endif // CERES_NO_CXSPARSE
218
219#ifndef CERES_NO_ACCELERATE_SPARSE
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800220TEST_F(SchurComplementSolverTest, SparseSchurWithAccelerateSparseSmallProblem) {
221 ComputeAndCompareSolutions(
222 2, false, SPARSE_SCHUR, EIGEN, ACCELERATE_SPARSE, true);
223 ComputeAndCompareSolutions(
224 2, true, SPARSE_SCHUR, EIGEN, ACCELERATE_SPARSE, true);
Austin Schuh70cc9552019-01-21 19:46:48 -0800225}
226
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800227TEST_F(SchurComplementSolverTest, SparseSchurWithAccelerateSparseLargeProblem) {
228 ComputeAndCompareSolutions(
229 3, false, SPARSE_SCHUR, EIGEN, ACCELERATE_SPARSE, true);
230 ComputeAndCompareSolutions(
231 3, true, SPARSE_SCHUR, EIGEN, ACCELERATE_SPARSE, true);
Austin Schuh70cc9552019-01-21 19:46:48 -0800232}
233#endif // CERES_NO_ACCELERATE_SPARSE
234
235#ifdef CERES_USE_EIGEN_SPARSE
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800236TEST_F(SchurComplementSolverTest, SparseSchurWithEigenSparseSmallProblem) {
Austin Schuh70cc9552019-01-21 19:46:48 -0800237 ComputeAndCompareSolutions(2, false, SPARSE_SCHUR, EIGEN, EIGEN_SPARSE, true);
238 ComputeAndCompareSolutions(2, true, SPARSE_SCHUR, EIGEN, EIGEN_SPARSE, true);
239}
240
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800241TEST_F(SchurComplementSolverTest, SparseSchurWithEigenSparseLargeProblem) {
Austin Schuh70cc9552019-01-21 19:46:48 -0800242 ComputeAndCompareSolutions(3, false, SPARSE_SCHUR, EIGEN, EIGEN_SPARSE, true);
243 ComputeAndCompareSolutions(3, true, SPARSE_SCHUR, EIGEN, EIGEN_SPARSE, true);
244}
245#endif // CERES_USE_EIGEN_SPARSE
246
247} // namespace internal
248} // namespace ceres