blob: 7c5ce2890d531dd51c7b093ab12a04e66911ff9d [file] [log] [blame]
Austin Schuh70cc9552019-01-21 19:46:48 -08001// Ceres Solver - A fast non-linear least squares minimizer
Austin Schuh3de38b02024-06-25 18:25:10 -07002// Copyright 2023 Google Inc. All rights reserved.
Austin Schuh70cc9552019-01-21 19:46:48 -08003// 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
Austin Schuh3de38b02024-06-25 18:25:10 -070048namespace ceres::internal {
Austin Schuh70cc9552019-01-21 19:46:48 -080049
50class SchurComplementSolverTest : public ::testing::Test {
51 protected:
52 void SetUpFromProblemId(int problem_id) {
Austin Schuh3de38b02024-06-25 18:25:10 -070053 std::unique_ptr<LinearLeastSquaresProblem> problem =
54 CreateLinearLeastSquaresProblemFromId(problem_id);
Austin Schuh70cc9552019-01-21 19:46:48 -080055
56 CHECK(problem != nullptr);
57 A.reset(down_cast<BlockSparseMatrix*>(problem->A.release()));
Austin Schuh3de38b02024-06-25 18:25:10 -070058 b = std::move(problem->b);
59 D = std::move(problem->D);
Austin Schuh70cc9552019-01-21 19:46:48 -080060
61 num_cols = A->num_cols();
62 num_rows = A->num_rows();
63 num_eliminate_blocks = problem->num_eliminate_blocks;
64
65 x.resize(num_cols);
66 sol.resize(num_cols);
67 sol_d.resize(num_cols);
68
69 LinearSolver::Options options;
70 options.type = DENSE_QR;
71 ContextImpl context;
72 options.context = &context;
73
74 std::unique_ptr<LinearSolver> qr(LinearSolver::Create(options));
75
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080076 TripletSparseMatrix triplet_A(
77 A->num_rows(), A->num_cols(), A->num_nonzeros());
Austin Schuh70cc9552019-01-21 19:46:48 -080078 A->ToTripletSparseMatrix(&triplet_A);
79
80 // Gold standard solutions using dense QR factorization.
81 DenseSparseMatrix dense_A(triplet_A);
82 qr->Solve(&dense_A, b.get(), LinearSolver::PerSolveOptions(), sol.data());
83
84 // Gold standard solution with appended diagonal.
85 LinearSolver::PerSolveOptions per_solve_options;
86 per_solve_options.D = D.get();
87 qr->Solve(&dense_A, b.get(), per_solve_options, sol_d.data());
88 }
89
90 void ComputeAndCompareSolutions(
91 int problem_id,
92 bool regularization,
93 ceres::LinearSolverType linear_solver_type,
94 ceres::DenseLinearAlgebraLibraryType dense_linear_algebra_library_type,
95 ceres::SparseLinearAlgebraLibraryType sparse_linear_algebra_library_type,
Austin Schuh3de38b02024-06-25 18:25:10 -070096 ceres::internal::OrderingType ordering_type) {
Austin Schuh70cc9552019-01-21 19:46:48 -080097 SetUpFromProblemId(problem_id);
98 LinearSolver::Options options;
99 options.elimination_groups.push_back(num_eliminate_blocks);
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800100 options.elimination_groups.push_back(A->block_structure()->cols.size() -
101 num_eliminate_blocks);
Austin Schuh70cc9552019-01-21 19:46:48 -0800102 options.type = linear_solver_type;
103 options.dense_linear_algebra_library_type =
104 dense_linear_algebra_library_type;
105 options.sparse_linear_algebra_library_type =
106 sparse_linear_algebra_library_type;
Austin Schuh3de38b02024-06-25 18:25:10 -0700107 options.ordering_type = ordering_type;
Austin Schuh70cc9552019-01-21 19:46:48 -0800108 ContextImpl context;
109 options.context = &context;
110 DetectStructure(*A->block_structure(),
111 num_eliminate_blocks,
112 &options.row_block_size,
113 &options.e_block_size,
114 &options.f_block_size);
115
116 std::unique_ptr<LinearSolver> solver(LinearSolver::Create(options));
117
118 LinearSolver::PerSolveOptions per_solve_options;
119 LinearSolver::Summary summary;
120 if (regularization) {
121 per_solve_options.D = D.get();
122 }
123
124 summary = solver->Solve(A.get(), b.get(), per_solve_options, x.data());
Austin Schuh3de38b02024-06-25 18:25:10 -0700125 EXPECT_EQ(summary.termination_type, LinearSolverTerminationType::SUCCESS);
Austin Schuh70cc9552019-01-21 19:46:48 -0800126
127 if (regularization) {
Austin Schuh70cc9552019-01-21 19:46:48 -0800128 ASSERT_NEAR((sol_d - x).norm() / num_cols, 0, 1e-10)
129 << "Regularized Expected solution: " << sol_d.transpose()
130 << " Actual solution: " << x.transpose();
131 } else {
132 ASSERT_NEAR((sol - x).norm() / num_cols, 0, 1e-10)
133 << "Unregularized Expected solution: " << sol.transpose()
134 << " Actual solution: " << x.transpose();
135 }
136 }
137
138 int num_rows;
139 int num_cols;
140 int num_eliminate_blocks;
141
142 std::unique_ptr<BlockSparseMatrix> A;
143 std::unique_ptr<double[]> b;
144 std::unique_ptr<double[]> D;
145 Vector x;
146 Vector sol;
147 Vector sol_d;
148};
149
150// TODO(sameeragarwal): Refactor these using value parameterized tests.
151// TODO(sameeragarwal): More extensive tests using random matrices.
152TEST_F(SchurComplementSolverTest, DenseSchurWithEigenSmallProblem) {
Austin Schuh3de38b02024-06-25 18:25:10 -0700153 ComputeAndCompareSolutions(
154 2, false, DENSE_SCHUR, EIGEN, SUITE_SPARSE, OrderingType::NATURAL);
155 ComputeAndCompareSolutions(
156 2, true, DENSE_SCHUR, EIGEN, SUITE_SPARSE, OrderingType::NATURAL);
Austin Schuh70cc9552019-01-21 19:46:48 -0800157}
158
159TEST_F(SchurComplementSolverTest, DenseSchurWithEigenLargeProblem) {
Austin Schuh3de38b02024-06-25 18:25:10 -0700160 ComputeAndCompareSolutions(
161 3, false, DENSE_SCHUR, EIGEN, SUITE_SPARSE, OrderingType::NATURAL);
162 ComputeAndCompareSolutions(
163 3, true, DENSE_SCHUR, EIGEN, SUITE_SPARSE, OrderingType::NATURAL);
Austin Schuh70cc9552019-01-21 19:46:48 -0800164}
165
166TEST_F(SchurComplementSolverTest, DenseSchurWithEigenVaryingFBlockSize) {
Austin Schuh3de38b02024-06-25 18:25:10 -0700167 ComputeAndCompareSolutions(
168 4, true, DENSE_SCHUR, EIGEN, SUITE_SPARSE, OrderingType::NATURAL);
Austin Schuh70cc9552019-01-21 19:46:48 -0800169}
170
171#ifndef CERES_NO_LAPACK
172TEST_F(SchurComplementSolverTest, DenseSchurWithLAPACKSmallProblem) {
Austin Schuh3de38b02024-06-25 18:25:10 -0700173 ComputeAndCompareSolutions(
174 2, false, DENSE_SCHUR, LAPACK, SUITE_SPARSE, OrderingType::NATURAL);
175 ComputeAndCompareSolutions(
176 2, true, DENSE_SCHUR, LAPACK, SUITE_SPARSE, OrderingType::NATURAL);
Austin Schuh70cc9552019-01-21 19:46:48 -0800177}
178
179TEST_F(SchurComplementSolverTest, DenseSchurWithLAPACKLargeProblem) {
Austin Schuh3de38b02024-06-25 18:25:10 -0700180 ComputeAndCompareSolutions(
181 3, false, DENSE_SCHUR, LAPACK, SUITE_SPARSE, OrderingType::NATURAL);
182 ComputeAndCompareSolutions(
183 3, true, DENSE_SCHUR, LAPACK, SUITE_SPARSE, OrderingType::NATURAL);
Austin Schuh70cc9552019-01-21 19:46:48 -0800184}
185#endif
186
187#ifndef CERES_NO_SUITESPARSE
188TEST_F(SchurComplementSolverTest,
Austin Schuh3de38b02024-06-25 18:25:10 -0700189 SparseSchurWithSuiteSparseSmallProblemNATURAL) {
Austin Schuh70cc9552019-01-21 19:46:48 -0800190 ComputeAndCompareSolutions(
Austin Schuh3de38b02024-06-25 18:25:10 -0700191 2, false, SPARSE_SCHUR, EIGEN, SUITE_SPARSE, OrderingType::NATURAL);
Austin Schuh70cc9552019-01-21 19:46:48 -0800192 ComputeAndCompareSolutions(
Austin Schuh3de38b02024-06-25 18:25:10 -0700193 2, true, SPARSE_SCHUR, EIGEN, SUITE_SPARSE, OrderingType::NATURAL);
Austin Schuh70cc9552019-01-21 19:46:48 -0800194}
195
196TEST_F(SchurComplementSolverTest,
Austin Schuh3de38b02024-06-25 18:25:10 -0700197 SparseSchurWithSuiteSparseLargeProblemNATURAL) {
198 ComputeAndCompareSolutions(
199 3, false, SPARSE_SCHUR, EIGEN, SUITE_SPARSE, OrderingType::NATURAL);
200 ComputeAndCompareSolutions(
201 3, true, SPARSE_SCHUR, EIGEN, SUITE_SPARSE, OrderingType::NATURAL);
Austin Schuh70cc9552019-01-21 19:46:48 -0800202}
Austin Schuh3de38b02024-06-25 18:25:10 -0700203
204TEST_F(SchurComplementSolverTest, SparseSchurWithSuiteSparseSmallProblemAMD) {
205 ComputeAndCompareSolutions(
206 2, false, SPARSE_SCHUR, EIGEN, SUITE_SPARSE, OrderingType::AMD);
207 ComputeAndCompareSolutions(
208 2, true, SPARSE_SCHUR, EIGEN, SUITE_SPARSE, OrderingType::AMD);
209}
210
211TEST_F(SchurComplementSolverTest, SparseSchurWithSuiteSparseLargeProblemAMD) {
212 ComputeAndCompareSolutions(
213 3, false, SPARSE_SCHUR, EIGEN, SUITE_SPARSE, OrderingType::AMD);
214 ComputeAndCompareSolutions(
215 3, true, SPARSE_SCHUR, EIGEN, SUITE_SPARSE, OrderingType::AMD);
216}
217
218#ifndef CERES_NO_EIGEN_METIS
219TEST_F(SchurComplementSolverTest,
220 SparseSchurWithSuiteSparseSmallProblemNESDIS) {
221 ComputeAndCompareSolutions(
222 2, false, SPARSE_SCHUR, EIGEN, SUITE_SPARSE, OrderingType::NESDIS);
223 ComputeAndCompareSolutions(
224 2, true, SPARSE_SCHUR, EIGEN, SUITE_SPARSE, OrderingType::NESDIS);
225}
226TEST_F(SchurComplementSolverTest,
227 SparseSchurWithSuiteSparseLargeProblemNESDIS) {
228 ComputeAndCompareSolutions(
229 3, false, SPARSE_SCHUR, EIGEN, SUITE_SPARSE, OrderingType::NESDIS);
230 ComputeAndCompareSolutions(
231 3, true, SPARSE_SCHUR, EIGEN, SUITE_SPARSE, OrderingType::NESDIS);
232}
233#endif // CERES_NO_EIGEN_METIS
Austin Schuh70cc9552019-01-21 19:46:48 -0800234#endif // CERES_NO_SUITESPARSE
235
Austin Schuh70cc9552019-01-21 19:46:48 -0800236#ifndef CERES_NO_ACCELERATE_SPARSE
Austin Schuh3de38b02024-06-25 18:25:10 -0700237TEST_F(SchurComplementSolverTest,
238 SparseSchurWithAccelerateSparseSmallProblemAMD) {
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800239 ComputeAndCompareSolutions(
Austin Schuh3de38b02024-06-25 18:25:10 -0700240 2, false, SPARSE_SCHUR, EIGEN, ACCELERATE_SPARSE, OrderingType::AMD);
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800241 ComputeAndCompareSolutions(
Austin Schuh3de38b02024-06-25 18:25:10 -0700242 2, true, SPARSE_SCHUR, EIGEN, ACCELERATE_SPARSE, OrderingType::AMD);
Austin Schuh70cc9552019-01-21 19:46:48 -0800243}
244
Austin Schuh3de38b02024-06-25 18:25:10 -0700245TEST_F(SchurComplementSolverTest,
246 SparseSchurWithAccelerateSparseSmallProblemNESDIS) {
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800247 ComputeAndCompareSolutions(
Austin Schuh3de38b02024-06-25 18:25:10 -0700248 2, false, SPARSE_SCHUR, EIGEN, ACCELERATE_SPARSE, OrderingType::NESDIS);
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800249 ComputeAndCompareSolutions(
Austin Schuh3de38b02024-06-25 18:25:10 -0700250 2, true, SPARSE_SCHUR, EIGEN, ACCELERATE_SPARSE, OrderingType::NESDIS);
251}
252
253TEST_F(SchurComplementSolverTest,
254 SparseSchurWithAccelerateSparseLargeProblemAMD) {
255 ComputeAndCompareSolutions(
256 3, false, SPARSE_SCHUR, EIGEN, ACCELERATE_SPARSE, OrderingType::AMD);
257 ComputeAndCompareSolutions(
258 3, true, SPARSE_SCHUR, EIGEN, ACCELERATE_SPARSE, OrderingType::AMD);
259}
260
261TEST_F(SchurComplementSolverTest,
262 SparseSchurWithAccelerateSparseLargeProblemNESDIS) {
263 ComputeAndCompareSolutions(
264 3, false, SPARSE_SCHUR, EIGEN, ACCELERATE_SPARSE, OrderingType::NESDIS);
265 ComputeAndCompareSolutions(
266 3, true, SPARSE_SCHUR, EIGEN, ACCELERATE_SPARSE, OrderingType::NESDIS);
Austin Schuh70cc9552019-01-21 19:46:48 -0800267}
268#endif // CERES_NO_ACCELERATE_SPARSE
269
270#ifdef CERES_USE_EIGEN_SPARSE
Austin Schuh3de38b02024-06-25 18:25:10 -0700271TEST_F(SchurComplementSolverTest, SparseSchurWithEigenSparseSmallProblemAMD) {
272 ComputeAndCompareSolutions(
273 2, false, SPARSE_SCHUR, EIGEN, EIGEN_SPARSE, OrderingType::AMD);
274 ComputeAndCompareSolutions(
275 2, true, SPARSE_SCHUR, EIGEN, EIGEN_SPARSE, OrderingType::AMD);
Austin Schuh70cc9552019-01-21 19:46:48 -0800276}
277
Austin Schuh3de38b02024-06-25 18:25:10 -0700278#ifndef CERES_NO_EIGEN_METIS
279TEST_F(SchurComplementSolverTest,
280 SparseSchurWithEigenSparseSmallProblemNESDIS) {
281 ComputeAndCompareSolutions(
282 2, false, SPARSE_SCHUR, EIGEN, EIGEN_SPARSE, OrderingType::NESDIS);
283 ComputeAndCompareSolutions(
284 2, true, SPARSE_SCHUR, EIGEN, EIGEN_SPARSE, OrderingType::NESDIS);
285}
286#endif
287
288TEST_F(SchurComplementSolverTest,
289 SparseSchurWithEigenSparseSmallProblemNATURAL) {
290 ComputeAndCompareSolutions(
291 2, false, SPARSE_SCHUR, EIGEN, EIGEN_SPARSE, OrderingType::NATURAL);
292 ComputeAndCompareSolutions(
293 2, true, SPARSE_SCHUR, EIGEN, EIGEN_SPARSE, OrderingType::NATURAL);
294}
295
296TEST_F(SchurComplementSolverTest, SparseSchurWithEigenSparseLargeProblemAMD) {
297 ComputeAndCompareSolutions(
298 3, false, SPARSE_SCHUR, EIGEN, EIGEN_SPARSE, OrderingType::AMD);
299 ComputeAndCompareSolutions(
300 3, true, SPARSE_SCHUR, EIGEN, EIGEN_SPARSE, OrderingType::AMD);
301}
302
303#ifndef CERES_NO_EIGEN_METIS
304TEST_F(SchurComplementSolverTest,
305 SparseSchurWithEigenSparseLargeProblemNESDIS) {
306 ComputeAndCompareSolutions(
307 3, false, SPARSE_SCHUR, EIGEN, EIGEN_SPARSE, OrderingType::NESDIS);
308 ComputeAndCompareSolutions(
309 3, true, SPARSE_SCHUR, EIGEN, EIGEN_SPARSE, OrderingType::NESDIS);
310}
311#endif
312
313TEST_F(SchurComplementSolverTest,
314 SparseSchurWithEigenSparseLargeProblemNATURAL) {
315 ComputeAndCompareSolutions(
316 3, false, SPARSE_SCHUR, EIGEN, EIGEN_SPARSE, OrderingType::NATURAL);
317 ComputeAndCompareSolutions(
318 3, true, SPARSE_SCHUR, EIGEN, EIGEN_SPARSE, OrderingType::NATURAL);
Austin Schuh70cc9552019-01-21 19:46:48 -0800319}
320#endif // CERES_USE_EIGEN_SPARSE
321
Austin Schuh3de38b02024-06-25 18:25:10 -0700322} // namespace ceres::internal