1 // Ceres Solver - A fast non-linear least squares minimizer
2 // Copyright 2015 Google Inc. All rights reserved.
3 // http://ceres-solver.org/
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are met:
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.
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.
29 // Author: sameeragarwal@google.com (Sameer Agarwal)
31 #include "ceres/solver.h"
36 #include "gtest/gtest.h"
37 #include "ceres/internal/scoped_ptr.h"
38 #include "ceres/autodiff_cost_function.h"
39 #include "ceres/sized_cost_function.h"
40 #include "ceres/problem.h"
41 #include "ceres/problem_impl.h"
48 TEST(SolverOptions, DefaultTrustRegionOptionsAreValid) {
49 Solver::Options options;
50 options.minimizer_type = TRUST_REGION;
52 EXPECT_TRUE(options.IsValid(&error)) << error;
55 TEST(SolverOptions, DefaultLineSearchOptionsAreValid) {
56 Solver::Options options;
57 options.minimizer_type = LINE_SEARCH;
59 EXPECT_TRUE(options.IsValid(&error)) << error;
62 struct QuadraticCostFunctor {
63 template <typename T> bool operator()(const T* const x,
65 residual[0] = T(5.0) - *x;
69 static CostFunction* Create() {
70 return new AutoDiffCostFunction<QuadraticCostFunctor, 1, 1>(
71 new QuadraticCostFunctor);
75 struct RememberingCallback : public IterationCallback {
76 explicit RememberingCallback(double *x) : calls(0), x(x) {}
77 virtual ~RememberingCallback() {}
78 virtual CallbackReturnType operator()(const IterationSummary& summary) {
79 x_values.push_back(*x);
80 return SOLVER_CONTINUE;
84 std::vector<double> x_values;
87 TEST(Solver, UpdateStateEveryIterationOption) {
89 const double original_x = x;
91 scoped_ptr<CostFunction> cost_function(QuadraticCostFunctor::Create());
92 Problem::Options problem_options;
93 problem_options.cost_function_ownership = DO_NOT_TAKE_OWNERSHIP;
94 Problem problem(problem_options);
95 problem.AddResidualBlock(cost_function.get(), NULL, &x);
97 Solver::Options options;
98 options.linear_solver_type = DENSE_QR;
100 RememberingCallback callback(&x);
101 options.callbacks.push_back(&callback);
103 Solver::Summary summary;
107 // First try: no updating.
108 Solve(options, &problem, &summary);
109 num_iterations = summary.num_successful_steps +
110 summary.num_unsuccessful_steps;
111 EXPECT_GT(num_iterations, 1);
112 for (int i = 0; i < callback.x_values.size(); ++i) {
113 EXPECT_EQ(50.0, callback.x_values[i]);
116 // Second try: with updating
118 options.update_state_every_iteration = true;
119 callback.x_values.clear();
120 Solve(options, &problem, &summary);
121 num_iterations = summary.num_successful_steps +
122 summary.num_unsuccessful_steps;
123 EXPECT_GT(num_iterations, 1);
124 EXPECT_EQ(original_x, callback.x_values[0]);
125 EXPECT_NE(original_x, callback.x_values[1]);
128 // The parameters must be in separate blocks so that they can be individually
129 // set constant or not.
130 struct Quadratic4DCostFunction {
131 template <typename T> bool operator()(const T* const x,
136 // A 4-dimension axis-aligned quadratic.
137 residual[0] = T(10.0) - *x +
144 static CostFunction* Create() {
145 return new AutoDiffCostFunction<Quadratic4DCostFunction, 1, 1, 1, 1, 1>(
146 new Quadratic4DCostFunction);
150 // A cost function that simply returns its argument.
151 class UnaryIdentityCostFunction : public SizedCostFunction<1, 1> {
153 virtual bool Evaluate(double const* const* parameters,
155 double** jacobians) const {
156 residuals[0] = parameters[0][0];
157 if (jacobians != NULL && jacobians[0] != NULL) {
158 jacobians[0][0] = 1.0;
164 TEST(Solver, TrustRegionProblemHasNoParameterBlocks) {
166 Solver::Options options;
167 options.minimizer_type = TRUST_REGION;
168 Solver::Summary summary;
169 Solve(options, &problem, &summary);
170 EXPECT_EQ(summary.termination_type, CONVERGENCE);
171 EXPECT_EQ(summary.message,
172 "Function tolerance reached. "
173 "No non-constant parameter blocks found.");
176 TEST(Solver, LineSearchProblemHasNoParameterBlocks) {
178 Solver::Options options;
179 options.minimizer_type = LINE_SEARCH;
180 Solver::Summary summary;
181 Solve(options, &problem, &summary);
182 EXPECT_EQ(summary.termination_type, CONVERGENCE);
183 EXPECT_EQ(summary.message,
184 "Function tolerance reached. "
185 "No non-constant parameter blocks found.");
188 TEST(Solver, TrustRegionProblemHasZeroResiduals) {
191 problem.AddParameterBlock(&x, 1);
192 Solver::Options options;
193 options.minimizer_type = TRUST_REGION;
194 Solver::Summary summary;
195 Solve(options, &problem, &summary);
196 EXPECT_EQ(summary.termination_type, CONVERGENCE);
197 EXPECT_EQ(summary.message,
198 "Function tolerance reached. "
199 "No non-constant parameter blocks found.");
202 TEST(Solver, LineSearchProblemHasZeroResiduals) {
205 problem.AddParameterBlock(&x, 1);
206 Solver::Options options;
207 options.minimizer_type = LINE_SEARCH;
208 Solver::Summary summary;
209 Solve(options, &problem, &summary);
210 EXPECT_EQ(summary.termination_type, CONVERGENCE);
211 EXPECT_EQ(summary.message,
212 "Function tolerance reached. "
213 "No non-constant parameter blocks found.");
216 TEST(Solver, TrustRegionProblemIsConstant) {
219 problem.AddResidualBlock(new UnaryIdentityCostFunction, NULL, &x);
220 problem.SetParameterBlockConstant(&x);
221 Solver::Options options;
222 options.minimizer_type = TRUST_REGION;
223 Solver::Summary summary;
224 Solve(options, &problem, &summary);
225 EXPECT_EQ(summary.termination_type, CONVERGENCE);
226 EXPECT_EQ(summary.initial_cost, 1.0 / 2.0);
227 EXPECT_EQ(summary.final_cost, 1.0 / 2.0);
230 TEST(Solver, LineSearchProblemIsConstant) {
233 problem.AddResidualBlock(new UnaryIdentityCostFunction, NULL, &x);
234 problem.SetParameterBlockConstant(&x);
235 Solver::Options options;
236 options.minimizer_type = LINE_SEARCH;
237 Solver::Summary summary;
238 Solve(options, &problem, &summary);
239 EXPECT_EQ(summary.termination_type, CONVERGENCE);
240 EXPECT_EQ(summary.initial_cost, 1.0 / 2.0);
241 EXPECT_EQ(summary.final_cost, 1.0 / 2.0);
244 #if defined(CERES_NO_SUITESPARSE)
245 TEST(Solver, SparseNormalCholeskyNoSuiteSparse) {
246 Solver::Options options;
247 options.sparse_linear_algebra_library_type = SUITE_SPARSE;
248 options.linear_solver_type = SPARSE_NORMAL_CHOLESKY;
250 EXPECT_FALSE(options.IsValid(&message));
253 TEST(Solver, SparseSchurNoSuiteSparse) {
254 Solver::Options options;
255 options.sparse_linear_algebra_library_type = SUITE_SPARSE;
256 options.linear_solver_type = SPARSE_SCHUR;
258 EXPECT_FALSE(options.IsValid(&message));
262 #if defined(CERES_NO_CXSPARSE)
263 TEST(Solver, SparseNormalCholeskyNoCXSparse) {
264 Solver::Options options;
265 options.sparse_linear_algebra_library_type = CX_SPARSE;
266 options.linear_solver_type = SPARSE_NORMAL_CHOLESKY;
268 EXPECT_FALSE(options.IsValid(&message));
271 TEST(Solver, SparseSchurNoCXSparse) {
272 Solver::Options options;
273 options.sparse_linear_algebra_library_type = CX_SPARSE;
274 options.linear_solver_type = SPARSE_SCHUR;
276 EXPECT_FALSE(options.IsValid(&message));
280 #if !defined(CERES_USE_EIGEN_SPARSE)
281 TEST(Solver, SparseNormalCholeskyNoEigenSparse) {
282 Solver::Options options;
283 options.sparse_linear_algebra_library_type = EIGEN_SPARSE;
284 options.linear_solver_type = SPARSE_NORMAL_CHOLESKY;
286 EXPECT_FALSE(options.IsValid(&message));
289 TEST(Solver, SparseSchurNoEigenSparse) {
290 Solver::Options options;
291 options.sparse_linear_algebra_library_type = EIGEN_SPARSE;
292 options.linear_solver_type = SPARSE_SCHUR;
294 EXPECT_FALSE(options.IsValid(&message));
298 TEST(Solver, SparseNormalCholeskyNoSparseLibrary) {
299 Solver::Options options;
300 options.sparse_linear_algebra_library_type = NO_SPARSE;
301 options.linear_solver_type = SPARSE_NORMAL_CHOLESKY;
303 EXPECT_FALSE(options.IsValid(&message));
306 TEST(Solver, SparseSchurNoSparseLibrary) {
307 Solver::Options options;
308 options.sparse_linear_algebra_library_type = NO_SPARSE;
309 options.linear_solver_type = SPARSE_SCHUR;
311 EXPECT_FALSE(options.IsValid(&message));
314 TEST(Solver, IterativeSchurWithClusterJacobiPerconditionerNoSparseLibrary) {
315 Solver::Options options;
316 options.sparse_linear_algebra_library_type = NO_SPARSE;
317 options.linear_solver_type = ITERATIVE_SCHUR;
318 // Requires SuiteSparse.
319 options.preconditioner_type = CLUSTER_JACOBI;
321 EXPECT_FALSE(options.IsValid(&message));
324 TEST(Solver, IterativeSchurWithClusterTridiagonalPerconditionerNoSparseLibrary) {
325 Solver::Options options;
326 options.sparse_linear_algebra_library_type = NO_SPARSE;
327 options.linear_solver_type = ITERATIVE_SCHUR;
328 // Requires SuiteSparse.
329 options.preconditioner_type = CLUSTER_TRIDIAGONAL;
331 EXPECT_FALSE(options.IsValid(&message));
334 TEST(Solver, IterativeLinearSolverForDogleg) {
335 Solver::Options options;
336 options.trust_region_strategy_type = DOGLEG;
338 options.linear_solver_type = ITERATIVE_SCHUR;
339 EXPECT_FALSE(options.IsValid(&message));
341 options.linear_solver_type = CGNR;
342 EXPECT_FALSE(options.IsValid(&message));
345 TEST(Solver, LinearSolverTypeNormalOperation) {
346 Solver::Options options;
347 options.linear_solver_type = DENSE_QR;
350 EXPECT_TRUE(options.IsValid(&message));
352 options.linear_solver_type = DENSE_NORMAL_CHOLESKY;
353 EXPECT_TRUE(options.IsValid(&message));
355 options.linear_solver_type = DENSE_SCHUR;
356 EXPECT_TRUE(options.IsValid(&message));
358 options.linear_solver_type = SPARSE_SCHUR;
359 #if defined(CERES_NO_SUITESPARSE) && \
360 defined(CERES_NO_CXSPARSE) && \
361 !defined(CERES_USE_EIGEN_SPARSE)
362 EXPECT_FALSE(options.IsValid(&message));
364 EXPECT_TRUE(options.IsValid(&message));
367 options.linear_solver_type = ITERATIVE_SCHUR;
368 EXPECT_TRUE(options.IsValid(&message));
371 template<int kNumResiduals, int N1 = 0, int N2 = 0, int N3 = 0>
372 class DummyCostFunction : public SizedCostFunction<kNumResiduals, N1, N2, N3> {
374 bool Evaluate(double const* const* parameters,
376 double** jacobians) const {
377 for (int i = 0; i < kNumResiduals; ++i) {
378 residuals[i] = kNumResiduals * kNumResiduals + i;
385 TEST(Solver, FixedCostForConstantProblem) {
388 problem.AddResidualBlock(new DummyCostFunction<2, 1>(), NULL, &x);
389 problem.SetParameterBlockConstant(&x);
390 const double expected_cost = 41.0 / 2.0; // 1/2 * ((4 + 0)^2 + (4 + 1)^2)
391 Solver::Options options;
392 Solver::Summary summary;
393 Solve(options, &problem, &summary);
394 EXPECT_TRUE(summary.IsSolutionUsable());
395 EXPECT_EQ(summary.fixed_cost, expected_cost);
396 EXPECT_EQ(summary.initial_cost, expected_cost);
397 EXPECT_EQ(summary.final_cost, expected_cost);
398 EXPECT_EQ(summary.iterations.size(), 0);
401 } // namespace internal