blob: cf3e9f6ef8ad9632710825d8d6bfb17cb9355bf3 [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/reorder_program.h"
32
33#include "ceres/parameter_block.h"
34#include "ceres/problem_impl.h"
35#include "ceres/program.h"
36#include "ceres/sized_cost_function.h"
37#include "ceres/solver.h"
38
39#include "gmock/gmock.h"
40#include "gtest/gtest.h"
41
42namespace ceres {
43namespace internal {
44
45using std::vector;
46
47// Templated base class for the CostFunction signatures.
48template <int kNumResiduals, int... Ns>
49class MockCostFunctionBase : public SizedCostFunction<kNumResiduals, Ns...> {
50 public:
51 virtual bool Evaluate(double const* const* parameters,
52 double* residuals,
53 double** jacobians) const {
54 // Do nothing. This is never called.
55 return true;
56 }
57};
58
59class UnaryCostFunction : public MockCostFunctionBase<2, 1> {};
60class BinaryCostFunction : public MockCostFunctionBase<2, 1, 1> {};
61class TernaryCostFunction : public MockCostFunctionBase<2, 1, 1, 1> {};
62
63TEST(_, ReorderResidualBlockNormalFunction) {
64 ProblemImpl problem;
65 double x;
66 double y;
67 double z;
68
69 problem.AddParameterBlock(&x, 1);
70 problem.AddParameterBlock(&y, 1);
71 problem.AddParameterBlock(&z, 1);
72
73 problem.AddResidualBlock(new UnaryCostFunction(), NULL, &x);
74 problem.AddResidualBlock(new BinaryCostFunction(), NULL, &z, &x);
75 problem.AddResidualBlock(new BinaryCostFunction(), NULL, &z, &y);
76 problem.AddResidualBlock(new UnaryCostFunction(), NULL, &z);
77 problem.AddResidualBlock(new BinaryCostFunction(), NULL, &x, &y);
78 problem.AddResidualBlock(new UnaryCostFunction(), NULL, &y);
79
80 ParameterBlockOrdering* linear_solver_ordering = new ParameterBlockOrdering;
81 linear_solver_ordering->AddElementToGroup(&x, 0);
82 linear_solver_ordering->AddElementToGroup(&y, 0);
83 linear_solver_ordering->AddElementToGroup(&z, 1);
84
85 Solver::Options options;
86 options.linear_solver_type = DENSE_SCHUR;
87 options.linear_solver_ordering.reset(linear_solver_ordering);
88
89 const vector<ResidualBlock*>& residual_blocks =
90 problem.program().residual_blocks();
91
92 vector<ResidualBlock*> expected_residual_blocks;
93
94 // This is a bit fragile, but it serves the purpose. We know the
95 // bucketing algorithm that the reordering function uses, so we
96 // expect the order for residual blocks for each e_block to be
97 // filled in reverse.
98 expected_residual_blocks.push_back(residual_blocks[4]);
99 expected_residual_blocks.push_back(residual_blocks[1]);
100 expected_residual_blocks.push_back(residual_blocks[0]);
101 expected_residual_blocks.push_back(residual_blocks[5]);
102 expected_residual_blocks.push_back(residual_blocks[2]);
103 expected_residual_blocks.push_back(residual_blocks[3]);
104
105 Program* program = problem.mutable_program();
106 program->SetParameterOffsetsAndIndex();
107
108 std::string message;
109 EXPECT_TRUE(LexicographicallyOrderResidualBlocks(
110 2,
111 problem.mutable_program(),
112 &message));
113 EXPECT_EQ(residual_blocks.size(), expected_residual_blocks.size());
114 for (int i = 0; i < expected_residual_blocks.size(); ++i) {
115 EXPECT_EQ(residual_blocks[i], expected_residual_blocks[i]);
116 }
117}
118
119TEST(_, ApplyOrderingOrderingTooSmall) {
120 ProblemImpl problem;
121 double x;
122 double y;
123 double z;
124
125 problem.AddParameterBlock(&x, 1);
126 problem.AddParameterBlock(&y, 1);
127 problem.AddParameterBlock(&z, 1);
128
129 ParameterBlockOrdering linear_solver_ordering;
130 linear_solver_ordering.AddElementToGroup(&x, 0);
131 linear_solver_ordering.AddElementToGroup(&y, 1);
132
133 Program program(problem.program());
134 std::string message;
135 EXPECT_FALSE(ApplyOrdering(problem.parameter_map(),
136 linear_solver_ordering,
137 &program,
138 &message));
139}
140
141TEST(_, ApplyOrderingNormal) {
142 ProblemImpl problem;
143 double x;
144 double y;
145 double z;
146
147 problem.AddParameterBlock(&x, 1);
148 problem.AddParameterBlock(&y, 1);
149 problem.AddParameterBlock(&z, 1);
150
151 ParameterBlockOrdering linear_solver_ordering;
152 linear_solver_ordering.AddElementToGroup(&x, 0);
153 linear_solver_ordering.AddElementToGroup(&y, 2);
154 linear_solver_ordering.AddElementToGroup(&z, 1);
155
156 Program* program = problem.mutable_program();
157 std::string message;
158
159 EXPECT_TRUE(ApplyOrdering(problem.parameter_map(),
160 linear_solver_ordering,
161 program,
162 &message));
163 const vector<ParameterBlock*>& parameter_blocks = program->parameter_blocks();
164
165 EXPECT_EQ(parameter_blocks.size(), 3);
166 EXPECT_EQ(parameter_blocks[0]->user_state(), &x);
167 EXPECT_EQ(parameter_blocks[1]->user_state(), &z);
168 EXPECT_EQ(parameter_blocks[2]->user_state(), &y);
169}
170
171#ifndef CERES_NO_SUITESPARSE
172class ReorderProgramForSparseNormalCholeskyUsingSuiteSparseTest :
173 public ::testing::Test {
174 protected:
175 void SetUp() {
176 problem_.AddResidualBlock(new UnaryCostFunction(), NULL, &x_);
177 problem_.AddResidualBlock(new BinaryCostFunction(), NULL, &z_, &x_);
178 problem_.AddResidualBlock(new BinaryCostFunction(), NULL, &z_, &y_);
179 problem_.AddResidualBlock(new UnaryCostFunction(), NULL, &z_);
180 problem_.AddResidualBlock(new BinaryCostFunction(), NULL, &x_, &y_);
181 problem_.AddResidualBlock(new UnaryCostFunction(), NULL, &y_);
182 }
183
184 void ComputeAndValidateOrdering(
185 const ParameterBlockOrdering& linear_solver_ordering) {
186 Program* program = problem_.mutable_program();
187 vector<ParameterBlock*> unordered_parameter_blocks =
188 program->parameter_blocks();
189
190 std::string error;
191 EXPECT_TRUE(ReorderProgramForSparseNormalCholesky(
192 ceres::SUITE_SPARSE,
193 linear_solver_ordering,
194 program,
195 &error));
196 const vector<ParameterBlock*>& ordered_parameter_blocks =
197 program->parameter_blocks();
198 EXPECT_EQ(ordered_parameter_blocks.size(),
199 unordered_parameter_blocks.size());
200
201 EXPECT_THAT(unordered_parameter_blocks,
202 ::testing::UnorderedElementsAreArray(ordered_parameter_blocks));
203 }
204
205 ProblemImpl problem_;
206 double x_;
207 double y_;
208 double z_;
209};
210
211TEST_F(ReorderProgramForSparseNormalCholeskyUsingSuiteSparseTest,
212 EverythingInGroupZero) {
213 ParameterBlockOrdering linear_solver_ordering;
214 linear_solver_ordering.AddElementToGroup(&x_, 0);
215 linear_solver_ordering.AddElementToGroup(&y_, 0);
216 linear_solver_ordering.AddElementToGroup(&z_, 0);
217
218 ComputeAndValidateOrdering(linear_solver_ordering);
219}
220
221TEST_F(ReorderProgramForSparseNormalCholeskyUsingSuiteSparseTest,
222 ContiguousGroups) {
223 ParameterBlockOrdering linear_solver_ordering;
224 linear_solver_ordering.AddElementToGroup(&x_, 0);
225 linear_solver_ordering.AddElementToGroup(&y_, 1);
226 linear_solver_ordering.AddElementToGroup(&z_, 2);
227
228 ComputeAndValidateOrdering(linear_solver_ordering);
229}
230
231TEST_F(ReorderProgramForSparseNormalCholeskyUsingSuiteSparseTest,
232 GroupsWithGaps) {
233 ParameterBlockOrdering linear_solver_ordering;
234 linear_solver_ordering.AddElementToGroup(&x_, 0);
235 linear_solver_ordering.AddElementToGroup(&y_, 2);
236 linear_solver_ordering.AddElementToGroup(&z_, 2);
237
238 ComputeAndValidateOrdering(linear_solver_ordering);
239}
240
241TEST_F(ReorderProgramForSparseNormalCholeskyUsingSuiteSparseTest,
242 NonContiguousStartingAtTwo) {
243 ParameterBlockOrdering linear_solver_ordering;
244 linear_solver_ordering.AddElementToGroup(&x_, 2);
245 linear_solver_ordering.AddElementToGroup(&y_, 4);
246 linear_solver_ordering.AddElementToGroup(&z_, 4);
247
248 ComputeAndValidateOrdering(linear_solver_ordering);
249}
250#endif // CERES_NO_SUITESPARSE
251
252} // namespace internal
253} // namespace ceres