blob: c4823be8d253cf7860a139674344356cd15a11b9 [file] [log] [blame]
Austin Schuh70cc9552019-01-21 19:46:48 -08001// Ceres Solver - A fast non-linear least squares minimizer
Austin Schuh1d1e6ea2020-12-23 21:56:30 -08002// Copyright 2019 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/solver.h"
32
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080033#include <cmath>
Austin Schuh70cc9552019-01-21 19:46:48 -080034#include <limits>
35#include <memory>
Austin Schuh70cc9552019-01-21 19:46:48 -080036#include <vector>
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080037
Austin Schuh70cc9552019-01-21 19:46:48 -080038#include "ceres/autodiff_cost_function.h"
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080039#include "ceres/evaluation_callback.h"
40#include "ceres/local_parameterization.h"
Austin Schuh70cc9552019-01-21 19:46:48 -080041#include "ceres/problem.h"
42#include "ceres/problem_impl.h"
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080043#include "ceres/sized_cost_function.h"
44#include "gtest/gtest.h"
Austin Schuh70cc9552019-01-21 19:46:48 -080045
46namespace ceres {
47namespace internal {
48
49using std::string;
50
51TEST(SolverOptions, DefaultTrustRegionOptionsAreValid) {
52 Solver::Options options;
53 options.minimizer_type = TRUST_REGION;
54 string error;
55 EXPECT_TRUE(options.IsValid(&error)) << error;
56}
57
58TEST(SolverOptions, DefaultLineSearchOptionsAreValid) {
59 Solver::Options options;
60 options.minimizer_type = LINE_SEARCH;
61 string error;
62 EXPECT_TRUE(options.IsValid(&error)) << error;
63}
64
65struct QuadraticCostFunctor {
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080066 template <typename T>
67 bool operator()(const T* const x, T* residual) const {
Austin Schuh70cc9552019-01-21 19:46:48 -080068 residual[0] = T(5.0) - *x;
69 return true;
70 }
71
72 static CostFunction* Create() {
73 return new AutoDiffCostFunction<QuadraticCostFunctor, 1, 1>(
74 new QuadraticCostFunctor);
75 }
76};
77
78struct RememberingCallback : public IterationCallback {
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080079 explicit RememberingCallback(double* x) : calls(0), x(x) {}
Austin Schuh70cc9552019-01-21 19:46:48 -080080 virtual ~RememberingCallback() {}
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080081 CallbackReturnType operator()(const IterationSummary& summary) final {
Austin Schuh70cc9552019-01-21 19:46:48 -080082 x_values.push_back(*x);
83 return SOLVER_CONTINUE;
84 }
85 int calls;
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080086 double* x;
Austin Schuh70cc9552019-01-21 19:46:48 -080087 std::vector<double> x_values;
88};
89
90struct NoOpEvaluationCallback : EvaluationCallback {
91 virtual ~NoOpEvaluationCallback() {}
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080092 void PrepareForEvaluation(bool evaluate_jacobians,
93 bool new_evaluation_point) final {
94 (void)evaluate_jacobians;
95 (void)new_evaluation_point;
Austin Schuh70cc9552019-01-21 19:46:48 -080096 }
97};
98
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080099TEST(Solver, UpdateStateEveryIterationOptionNoEvaluationCallback) {
Austin Schuh70cc9552019-01-21 19:46:48 -0800100 double x = 50.0;
101 const double original_x = x;
102
Austin Schuh70cc9552019-01-21 19:46:48 -0800103 Problem::Options problem_options;
Austin Schuh70cc9552019-01-21 19:46:48 -0800104 Problem problem(problem_options);
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800105 problem.AddResidualBlock(QuadraticCostFunctor::Create(), nullptr, &x);
Austin Schuh70cc9552019-01-21 19:46:48 -0800106
107 Solver::Options options;
108 options.linear_solver_type = DENSE_QR;
109
110 RememberingCallback callback(&x);
111 options.callbacks.push_back(&callback);
112
113 Solver::Summary summary;
114
115 int num_iterations;
116
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800117 // First: update_state_every_iteration=false, evaluation_callback=nullptr.
Austin Schuh70cc9552019-01-21 19:46:48 -0800118 Solve(options, &problem, &summary);
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800119 num_iterations =
120 summary.num_successful_steps + summary.num_unsuccessful_steps;
Austin Schuh70cc9552019-01-21 19:46:48 -0800121 EXPECT_GT(num_iterations, 1);
122 for (int i = 0; i < callback.x_values.size(); ++i) {
123 EXPECT_EQ(50.0, callback.x_values[i]);
124 }
125
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800126 // Second: update_state_every_iteration=true, evaluation_callback=nullptr.
Austin Schuh70cc9552019-01-21 19:46:48 -0800127 x = 50.0;
128 options.update_state_every_iteration = true;
129 callback.x_values.clear();
130 Solve(options, &problem, &summary);
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800131 num_iterations =
132 summary.num_successful_steps + summary.num_unsuccessful_steps;
Austin Schuh70cc9552019-01-21 19:46:48 -0800133 EXPECT_GT(num_iterations, 1);
134 EXPECT_EQ(original_x, callback.x_values[0]);
135 EXPECT_NE(original_x, callback.x_values[1]);
136}
137
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800138TEST(Solver, UpdateStateEveryIterationOptionWithEvaluationCallback) {
139 double x = 50.0;
140 const double original_x = x;
141
142 Problem::Options problem_options;
143 NoOpEvaluationCallback evaluation_callback;
144 problem_options.evaluation_callback = &evaluation_callback;
145
146 Problem problem(problem_options);
147 problem.AddResidualBlock(QuadraticCostFunctor::Create(), nullptr, &x);
148
149 Solver::Options options;
150 options.linear_solver_type = DENSE_QR;
151 RememberingCallback callback(&x);
152 options.callbacks.push_back(&callback);
153
154 Solver::Summary summary;
155
156 int num_iterations;
157
158 // First: update_state_every_iteration=true, evaluation_callback=!nullptr.
159 x = 50.0;
160 options.update_state_every_iteration = true;
161 callback.x_values.clear();
162 Solve(options, &problem, &summary);
163 num_iterations =
164 summary.num_successful_steps + summary.num_unsuccessful_steps;
165 EXPECT_GT(num_iterations, 1);
166 EXPECT_EQ(original_x, callback.x_values[0]);
167 EXPECT_NE(original_x, callback.x_values[1]);
168
169 // Second: update_state_every_iteration=false, evaluation_callback=!nullptr.
170 x = 50.0;
171 options.update_state_every_iteration = false;
172 callback.x_values.clear();
173 Solve(options, &problem, &summary);
174 num_iterations =
175 summary.num_successful_steps + summary.num_unsuccessful_steps;
176 EXPECT_GT(num_iterations, 1);
177 EXPECT_EQ(original_x, callback.x_values[0]);
178 EXPECT_NE(original_x, callback.x_values[1]);
179}
180
181TEST(Solver, CantMixEvaluationCallbackWithInnerIterations) {
182 double x = 50.0;
183 double y = 60.0;
184
185 Problem::Options problem_options;
186 NoOpEvaluationCallback evaluation_callback;
187 problem_options.evaluation_callback = &evaluation_callback;
188
189 Problem problem(problem_options);
190 problem.AddResidualBlock(QuadraticCostFunctor::Create(), nullptr, &x);
191 problem.AddResidualBlock(QuadraticCostFunctor::Create(), nullptr, &y);
192
193 Solver::Options options;
194 options.use_inner_iterations = true;
195 Solver::Summary summary;
196 Solve(options, &problem, &summary);
197 EXPECT_EQ(summary.termination_type, FAILURE);
198
199 options.use_inner_iterations = false;
200 Solve(options, &problem, &summary);
201 EXPECT_EQ(summary.termination_type, CONVERGENCE);
202}
203
Austin Schuh70cc9552019-01-21 19:46:48 -0800204// The parameters must be in separate blocks so that they can be individually
205// set constant or not.
206struct Quadratic4DCostFunction {
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800207 template <typename T>
208 bool operator()(const T* const x,
209 const T* const y,
210 const T* const z,
211 const T* const w,
212 T* residual) const {
Austin Schuh70cc9552019-01-21 19:46:48 -0800213 // A 4-dimension axis-aligned quadratic.
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800214 residual[0] = T(10.0) - *x + T(20.0) - *y + T(30.0) - *z + T(40.0) - *w;
Austin Schuh70cc9552019-01-21 19:46:48 -0800215 return true;
216 }
217
218 static CostFunction* Create() {
219 return new AutoDiffCostFunction<Quadratic4DCostFunction, 1, 1, 1, 1, 1>(
220 new Quadratic4DCostFunction);
221 }
222};
223
224// A cost function that simply returns its argument.
225class UnaryIdentityCostFunction : public SizedCostFunction<1, 1> {
226 public:
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800227 bool Evaluate(double const* const* parameters,
228 double* residuals,
229 double** jacobians) const final {
Austin Schuh70cc9552019-01-21 19:46:48 -0800230 residuals[0] = parameters[0][0];
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800231 if (jacobians != nullptr && jacobians[0] != nullptr) {
Austin Schuh70cc9552019-01-21 19:46:48 -0800232 jacobians[0][0] = 1.0;
233 }
234 return true;
235 }
236};
237
238TEST(Solver, TrustRegionProblemHasNoParameterBlocks) {
239 Problem problem;
240 Solver::Options options;
241 options.minimizer_type = TRUST_REGION;
242 Solver::Summary summary;
243 Solve(options, &problem, &summary);
244 EXPECT_EQ(summary.termination_type, CONVERGENCE);
245 EXPECT_EQ(summary.message,
246 "Function tolerance reached. "
247 "No non-constant parameter blocks found.");
248}
249
250TEST(Solver, LineSearchProblemHasNoParameterBlocks) {
251 Problem problem;
252 Solver::Options options;
253 options.minimizer_type = LINE_SEARCH;
254 Solver::Summary summary;
255 Solve(options, &problem, &summary);
256 EXPECT_EQ(summary.termination_type, CONVERGENCE);
257 EXPECT_EQ(summary.message,
258 "Function tolerance reached. "
259 "No non-constant parameter blocks found.");
260}
261
262TEST(Solver, TrustRegionProblemHasZeroResiduals) {
263 Problem problem;
264 double x = 1;
265 problem.AddParameterBlock(&x, 1);
266 Solver::Options options;
267 options.minimizer_type = TRUST_REGION;
268 Solver::Summary summary;
269 Solve(options, &problem, &summary);
270 EXPECT_EQ(summary.termination_type, CONVERGENCE);
271 EXPECT_EQ(summary.message,
272 "Function tolerance reached. "
273 "No non-constant parameter blocks found.");
274}
275
276TEST(Solver, LineSearchProblemHasZeroResiduals) {
277 Problem problem;
278 double x = 1;
279 problem.AddParameterBlock(&x, 1);
280 Solver::Options options;
281 options.minimizer_type = LINE_SEARCH;
282 Solver::Summary summary;
283 Solve(options, &problem, &summary);
284 EXPECT_EQ(summary.termination_type, CONVERGENCE);
285 EXPECT_EQ(summary.message,
286 "Function tolerance reached. "
287 "No non-constant parameter blocks found.");
288}
289
290TEST(Solver, TrustRegionProblemIsConstant) {
291 Problem problem;
292 double x = 1;
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800293 problem.AddResidualBlock(new UnaryIdentityCostFunction, nullptr, &x);
Austin Schuh70cc9552019-01-21 19:46:48 -0800294 problem.SetParameterBlockConstant(&x);
295 Solver::Options options;
296 options.minimizer_type = TRUST_REGION;
297 Solver::Summary summary;
298 Solve(options, &problem, &summary);
299 EXPECT_EQ(summary.termination_type, CONVERGENCE);
300 EXPECT_EQ(summary.initial_cost, 1.0 / 2.0);
301 EXPECT_EQ(summary.final_cost, 1.0 / 2.0);
302}
303
304TEST(Solver, LineSearchProblemIsConstant) {
305 Problem problem;
306 double x = 1;
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800307 problem.AddResidualBlock(new UnaryIdentityCostFunction, nullptr, &x);
Austin Schuh70cc9552019-01-21 19:46:48 -0800308 problem.SetParameterBlockConstant(&x);
309 Solver::Options options;
310 options.minimizer_type = LINE_SEARCH;
311 Solver::Summary summary;
312 Solve(options, &problem, &summary);
313 EXPECT_EQ(summary.termination_type, CONVERGENCE);
314 EXPECT_EQ(summary.initial_cost, 1.0 / 2.0);
315 EXPECT_EQ(summary.final_cost, 1.0 / 2.0);
316}
317
318#if defined(CERES_NO_SUITESPARSE)
319TEST(Solver, SparseNormalCholeskyNoSuiteSparse) {
320 Solver::Options options;
321 options.sparse_linear_algebra_library_type = SUITE_SPARSE;
322 options.linear_solver_type = SPARSE_NORMAL_CHOLESKY;
323 string message;
324 EXPECT_FALSE(options.IsValid(&message));
325}
326
327TEST(Solver, SparseSchurNoSuiteSparse) {
328 Solver::Options options;
329 options.sparse_linear_algebra_library_type = SUITE_SPARSE;
330 options.linear_solver_type = SPARSE_SCHUR;
331 string message;
332 EXPECT_FALSE(options.IsValid(&message));
333}
334#endif
335
336#if defined(CERES_NO_CXSPARSE)
337TEST(Solver, SparseNormalCholeskyNoCXSparse) {
338 Solver::Options options;
339 options.sparse_linear_algebra_library_type = CX_SPARSE;
340 options.linear_solver_type = SPARSE_NORMAL_CHOLESKY;
341 string message;
342 EXPECT_FALSE(options.IsValid(&message));
343}
344
345TEST(Solver, SparseSchurNoCXSparse) {
346 Solver::Options options;
347 options.sparse_linear_algebra_library_type = CX_SPARSE;
348 options.linear_solver_type = SPARSE_SCHUR;
349 string message;
350 EXPECT_FALSE(options.IsValid(&message));
351}
352#endif
353
354#if defined(CERES_NO_ACCELERATE_SPARSE)
355TEST(Solver, SparseNormalCholeskyNoAccelerateSparse) {
356 Solver::Options options;
357 options.sparse_linear_algebra_library_type = ACCELERATE_SPARSE;
358 options.linear_solver_type = SPARSE_NORMAL_CHOLESKY;
359 string message;
360 EXPECT_FALSE(options.IsValid(&message));
361}
362
363TEST(Solver, SparseSchurNoAccelerateSparse) {
364 Solver::Options options;
365 options.sparse_linear_algebra_library_type = ACCELERATE_SPARSE;
366 options.linear_solver_type = SPARSE_SCHUR;
367 string message;
368 EXPECT_FALSE(options.IsValid(&message));
369}
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800370#else
371TEST(Solver, DynamicSparseNormalCholeskyUnsupportedWithAccelerateSparse) {
372 Solver::Options options;
373 options.sparse_linear_algebra_library_type = ACCELERATE_SPARSE;
374 options.linear_solver_type = SPARSE_NORMAL_CHOLESKY;
375 options.dynamic_sparsity = true;
376 string message;
377 EXPECT_FALSE(options.IsValid(&message));
378}
Austin Schuh70cc9552019-01-21 19:46:48 -0800379#endif
380
381#if !defined(CERES_USE_EIGEN_SPARSE)
382TEST(Solver, SparseNormalCholeskyNoEigenSparse) {
383 Solver::Options options;
384 options.sparse_linear_algebra_library_type = EIGEN_SPARSE;
385 options.linear_solver_type = SPARSE_NORMAL_CHOLESKY;
386 string message;
387 EXPECT_FALSE(options.IsValid(&message));
388}
389
390TEST(Solver, SparseSchurNoEigenSparse) {
391 Solver::Options options;
392 options.sparse_linear_algebra_library_type = EIGEN_SPARSE;
393 options.linear_solver_type = SPARSE_SCHUR;
394 string message;
395 EXPECT_FALSE(options.IsValid(&message));
396}
397#endif
398
399TEST(Solver, SparseNormalCholeskyNoSparseLibrary) {
400 Solver::Options options;
401 options.sparse_linear_algebra_library_type = NO_SPARSE;
402 options.linear_solver_type = SPARSE_NORMAL_CHOLESKY;
403 string message;
404 EXPECT_FALSE(options.IsValid(&message));
405}
406
407TEST(Solver, SparseSchurNoSparseLibrary) {
408 Solver::Options options;
409 options.sparse_linear_algebra_library_type = NO_SPARSE;
410 options.linear_solver_type = SPARSE_SCHUR;
411 string message;
412 EXPECT_FALSE(options.IsValid(&message));
413}
414
415TEST(Solver, IterativeSchurWithClusterJacobiPerconditionerNoSparseLibrary) {
416 Solver::Options options;
417 options.sparse_linear_algebra_library_type = NO_SPARSE;
418 options.linear_solver_type = ITERATIVE_SCHUR;
419 // Requires SuiteSparse.
420 options.preconditioner_type = CLUSTER_JACOBI;
421 string message;
422 EXPECT_FALSE(options.IsValid(&message));
423}
424
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800425TEST(Solver,
426 IterativeSchurWithClusterTridiagonalPerconditionerNoSparseLibrary) {
Austin Schuh70cc9552019-01-21 19:46:48 -0800427 Solver::Options options;
428 options.sparse_linear_algebra_library_type = NO_SPARSE;
429 options.linear_solver_type = ITERATIVE_SCHUR;
430 // Requires SuiteSparse.
431 options.preconditioner_type = CLUSTER_TRIDIAGONAL;
432 string message;
433 EXPECT_FALSE(options.IsValid(&message));
434}
435
436TEST(Solver, IterativeLinearSolverForDogleg) {
437 Solver::Options options;
438 options.trust_region_strategy_type = DOGLEG;
439 string message;
440 options.linear_solver_type = ITERATIVE_SCHUR;
441 EXPECT_FALSE(options.IsValid(&message));
442
443 options.linear_solver_type = CGNR;
444 EXPECT_FALSE(options.IsValid(&message));
445}
446
447TEST(Solver, LinearSolverTypeNormalOperation) {
448 Solver::Options options;
449 options.linear_solver_type = DENSE_QR;
450
451 string message;
452 EXPECT_TRUE(options.IsValid(&message));
453
454 options.linear_solver_type = DENSE_NORMAL_CHOLESKY;
455 EXPECT_TRUE(options.IsValid(&message));
456
457 options.linear_solver_type = DENSE_SCHUR;
458 EXPECT_TRUE(options.IsValid(&message));
459
460 options.linear_solver_type = SPARSE_SCHUR;
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800461#if defined(CERES_NO_SUITESPARSE) && defined(CERES_NO_CXSPARSE) && \
462 !defined(CERES_USE_EIGEN_SPARSE)
Austin Schuh70cc9552019-01-21 19:46:48 -0800463 EXPECT_FALSE(options.IsValid(&message));
464#else
465 EXPECT_TRUE(options.IsValid(&message));
466#endif
467
468 options.linear_solver_type = ITERATIVE_SCHUR;
469 EXPECT_TRUE(options.IsValid(&message));
470}
471
Austin Schuh70cc9552019-01-21 19:46:48 -0800472template <int kNumResiduals, int... Ns>
473class DummyCostFunction : public SizedCostFunction<kNumResiduals, Ns...> {
474 public:
475 bool Evaluate(double const* const* parameters,
476 double* residuals,
477 double** jacobians) const {
478 for (int i = 0; i < kNumResiduals; ++i) {
479 residuals[i] = kNumResiduals * kNumResiduals + i;
480 }
481
482 return true;
483 }
484};
485
486TEST(Solver, FixedCostForConstantProblem) {
487 double x = 1.0;
488 Problem problem;
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800489 problem.AddResidualBlock(new DummyCostFunction<2, 1>(), nullptr, &x);
Austin Schuh70cc9552019-01-21 19:46:48 -0800490 problem.SetParameterBlockConstant(&x);
491 const double expected_cost = 41.0 / 2.0; // 1/2 * ((4 + 0)^2 + (4 + 1)^2)
492 Solver::Options options;
493 Solver::Summary summary;
494 Solve(options, &problem, &summary);
495 EXPECT_TRUE(summary.IsSolutionUsable());
496 EXPECT_EQ(summary.fixed_cost, expected_cost);
497 EXPECT_EQ(summary.initial_cost, expected_cost);
498 EXPECT_EQ(summary.final_cost, expected_cost);
499 EXPECT_EQ(summary.iterations.size(), 0);
500}
501
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800502struct LinearCostFunction {
503 template <typename T>
504 bool operator()(const T* x, const T* y, T* residual) const {
505 residual[0] = T(10.0) - *x;
506 residual[1] = T(5.0) - *y;
507 return true;
508 }
509 static CostFunction* Create() {
510 return new AutoDiffCostFunction<LinearCostFunction, 2, 1, 1>(
511 new LinearCostFunction);
512 }
513};
514
515TEST(Solver, ZeroSizedLocalParameterizationHoldsParameterBlockConstant) {
516 double x = 0.0;
517 double y = 1.0;
518 Problem problem;
519 problem.AddResidualBlock(LinearCostFunction::Create(), nullptr, &x, &y);
520 problem.SetParameterization(&y, new SubsetParameterization(1, {0}));
521 EXPECT_TRUE(problem.IsParameterBlockConstant(&y));
522
523 Solver::Options options;
524 options.function_tolerance = 0.0;
525 options.gradient_tolerance = 0.0;
526 options.parameter_tolerance = 0.0;
527 Solver::Summary summary;
528 Solve(options, &problem, &summary);
529
530 EXPECT_EQ(summary.termination_type, CONVERGENCE);
531 EXPECT_NEAR(x, 10.0, 1e-7);
532 EXPECT_EQ(y, 1.0);
533}
534
Austin Schuh70cc9552019-01-21 19:46:48 -0800535} // namespace internal
536} // namespace ceres