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/program.h"
36 #include "ceres/sized_cost_function.h"
37 #include "ceres/problem_impl.h"
38 #include "ceres/residual_block.h"
39 #include "ceres/triplet_sparse_matrix.h"
40 #include "gtest/gtest.h"
48 // A cost function that simply returns its argument.
49 class UnaryIdentityCostFunction : public SizedCostFunction<1, 1> {
51 virtual bool Evaluate(double const* const* parameters,
53 double** jacobians) const {
54 residuals[0] = parameters[0][0];
55 if (jacobians != NULL && jacobians[0] != NULL) {
56 jacobians[0][0] = 1.0;
62 // Templated base class for the CostFunction signatures.
63 template <int kNumResiduals, int N0, int N1, int N2>
64 class MockCostFunctionBase : public
65 SizedCostFunction<kNumResiduals, N0, N1, N2> {
67 virtual bool Evaluate(double const* const* parameters,
69 double** jacobians) const {
70 for (int i = 0; i < kNumResiduals; ++i) {
71 residuals[i] = kNumResiduals + N0 + N1 + N2;
77 class UnaryCostFunction : public MockCostFunctionBase<2, 1, 0, 0> {};
78 class BinaryCostFunction : public MockCostFunctionBase<2, 1, 1, 0> {};
79 class TernaryCostFunction : public MockCostFunctionBase<2, 1, 1, 1> {};
81 TEST(Program, RemoveFixedBlocksNothingConstant) {
87 problem.AddParameterBlock(&x, 1);
88 problem.AddParameterBlock(&y, 1);
89 problem.AddParameterBlock(&z, 1);
90 problem.AddResidualBlock(new UnaryCostFunction(), NULL, &x);
91 problem.AddResidualBlock(new BinaryCostFunction(), NULL, &x, &y);
92 problem.AddResidualBlock(new TernaryCostFunction(), NULL, &x, &y, &z);
94 vector<double*> removed_parameter_blocks;
95 double fixed_cost = 0.0;
97 scoped_ptr<Program> reduced_program(
100 .CreateReducedProgram(&removed_parameter_blocks,
104 EXPECT_EQ(reduced_program->NumParameterBlocks(), 3);
105 EXPECT_EQ(reduced_program->NumResidualBlocks(), 3);
106 EXPECT_EQ(removed_parameter_blocks.size(), 0);
107 EXPECT_EQ(fixed_cost, 0.0);
110 TEST(Program, RemoveFixedBlocksAllParameterBlocksConstant) {
114 problem.AddParameterBlock(&x, 1);
115 problem.AddResidualBlock(new UnaryCostFunction(), NULL, &x);
116 problem.SetParameterBlockConstant(&x);
118 vector<double*> removed_parameter_blocks;
119 double fixed_cost = 0.0;
121 scoped_ptr<Program> reduced_program(
122 CHECK_NOTNULL(problem
124 .CreateReducedProgram(&removed_parameter_blocks,
127 EXPECT_EQ(reduced_program->NumParameterBlocks(), 0);
128 EXPECT_EQ(reduced_program->NumResidualBlocks(), 0);
129 EXPECT_EQ(removed_parameter_blocks.size(), 1);
130 EXPECT_EQ(removed_parameter_blocks[0], &x);
131 EXPECT_EQ(fixed_cost, 9.0);
135 TEST(Program, RemoveFixedBlocksNoResidualBlocks) {
141 problem.AddParameterBlock(&x, 1);
142 problem.AddParameterBlock(&y, 1);
143 problem.AddParameterBlock(&z, 1);
145 vector<double*> removed_parameter_blocks;
146 double fixed_cost = 0.0;
148 scoped_ptr<Program> reduced_program(
149 CHECK_NOTNULL(problem
151 .CreateReducedProgram(&removed_parameter_blocks,
154 EXPECT_EQ(reduced_program->NumParameterBlocks(), 0);
155 EXPECT_EQ(reduced_program->NumResidualBlocks(), 0);
156 EXPECT_EQ(removed_parameter_blocks.size(), 3);
157 EXPECT_EQ(fixed_cost, 0.0);
160 TEST(Program, RemoveFixedBlocksOneParameterBlockConstant) {
166 problem.AddParameterBlock(&x, 1);
167 problem.AddParameterBlock(&y, 1);
168 problem.AddParameterBlock(&z, 1);
170 problem.AddResidualBlock(new UnaryCostFunction(), NULL, &x);
171 problem.AddResidualBlock(new BinaryCostFunction(), NULL, &x, &y);
172 problem.SetParameterBlockConstant(&x);
174 vector<double*> removed_parameter_blocks;
175 double fixed_cost = 0.0;
177 scoped_ptr<Program> reduced_program(
178 CHECK_NOTNULL(problem
180 .CreateReducedProgram(&removed_parameter_blocks,
183 EXPECT_EQ(reduced_program->NumParameterBlocks(), 1);
184 EXPECT_EQ(reduced_program->NumResidualBlocks(), 1);
187 TEST(Program, RemoveFixedBlocksNumEliminateBlocks) {
193 problem.AddParameterBlock(&x, 1);
194 problem.AddParameterBlock(&y, 1);
195 problem.AddParameterBlock(&z, 1);
196 problem.AddResidualBlock(new UnaryCostFunction(), NULL, &x);
197 problem.AddResidualBlock(new TernaryCostFunction(), NULL, &x, &y, &z);
198 problem.AddResidualBlock(new BinaryCostFunction(), NULL, &x, &y);
199 problem.SetParameterBlockConstant(&x);
201 vector<double*> removed_parameter_blocks;
202 double fixed_cost = 0.0;
204 scoped_ptr<Program> reduced_program(
205 CHECK_NOTNULL(problem
207 .CreateReducedProgram(&removed_parameter_blocks,
210 EXPECT_EQ(reduced_program->NumParameterBlocks(), 2);
211 EXPECT_EQ(reduced_program->NumResidualBlocks(), 2);
214 TEST(Program, RemoveFixedBlocksFixedCost) {
220 problem.AddParameterBlock(&x, 1);
221 problem.AddParameterBlock(&y, 1);
222 problem.AddParameterBlock(&z, 1);
223 problem.AddResidualBlock(new UnaryIdentityCostFunction(), NULL, &x);
224 problem.AddResidualBlock(new TernaryCostFunction(), NULL, &x, &y, &z);
225 problem.AddResidualBlock(new BinaryCostFunction(), NULL, &x, &y);
226 problem.SetParameterBlockConstant(&x);
228 ResidualBlock *expected_removed_block =
229 problem.program().residual_blocks()[0];
230 scoped_array<double> scratch(
231 new double[expected_removed_block->NumScratchDoublesForEvaluate()]);
232 double expected_fixed_cost;
233 expected_removed_block->Evaluate(true,
234 &expected_fixed_cost,
240 vector<double*> removed_parameter_blocks;
241 double fixed_cost = 0.0;
243 scoped_ptr<Program> reduced_program(
244 CHECK_NOTNULL(problem
246 .CreateReducedProgram(&removed_parameter_blocks,
250 EXPECT_EQ(reduced_program->NumParameterBlocks(), 2);
251 EXPECT_EQ(reduced_program->NumResidualBlocks(), 2);
252 EXPECT_DOUBLE_EQ(fixed_cost, expected_fixed_cost);
255 TEST(Program, CreateJacobianBlockSparsityTranspose) {
261 problem.AddParameterBlock(x, 2);
262 problem.AddParameterBlock(y, 3);
263 problem.AddParameterBlock(&z, 1);
265 problem.AddResidualBlock(new MockCostFunctionBase<2, 2, 0, 0>(), NULL, x);
266 problem.AddResidualBlock(new MockCostFunctionBase<3, 1, 2, 0>(), NULL, &z, x);
267 problem.AddResidualBlock(new MockCostFunctionBase<4, 1, 3, 0>(), NULL, &z, y);
268 problem.AddResidualBlock(new MockCostFunctionBase<5, 1, 3, 0>(), NULL, &z, y);
269 problem.AddResidualBlock(new MockCostFunctionBase<1, 2, 1, 0>(), NULL, x, &z);
270 problem.AddResidualBlock(new MockCostFunctionBase<2, 1, 3, 0>(), NULL, &z, y);
271 problem.AddResidualBlock(new MockCostFunctionBase<2, 2, 1, 0>(), NULL, x, &z);
272 problem.AddResidualBlock(new MockCostFunctionBase<1, 3, 0, 0>(), NULL, y);
274 TripletSparseMatrix expected_block_sparse_jacobian(3, 8, 14);
276 int* rows = expected_block_sparse_jacobian.mutable_rows();
277 int* cols = expected_block_sparse_jacobian.mutable_cols();
278 double* values = expected_block_sparse_jacobian.mutable_values();
314 std::fill(values, values + 14, 1.0);
315 expected_block_sparse_jacobian.set_num_nonzeros(14);
318 Program* program = problem.mutable_program();
319 program->SetParameterOffsetsAndIndex();
321 scoped_ptr<TripletSparseMatrix> actual_block_sparse_jacobian(
322 program->CreateJacobianBlockSparsityTranspose());
324 Matrix expected_dense_jacobian;
325 expected_block_sparse_jacobian.ToDenseMatrix(&expected_dense_jacobian);
327 Matrix actual_dense_jacobian;
328 actual_block_sparse_jacobian->ToDenseMatrix(&actual_dense_jacobian);
329 EXPECT_EQ((expected_dense_jacobian - actual_dense_jacobian).norm(), 0.0);
332 template <int kNumResiduals, int kNumParameterBlocks>
333 class NumParameterBlocksCostFunction : public CostFunction {
335 NumParameterBlocksCostFunction() {
336 set_num_residuals(kNumResiduals);
337 for (int i = 0; i < kNumParameterBlocks; ++i) {
338 mutable_parameter_block_sizes()->push_back(1);
342 virtual ~NumParameterBlocksCostFunction() {
345 virtual bool Evaluate(double const* const* parameters,
347 double** jacobians) const {
352 TEST(Program, ReallocationInCreateJacobianBlockSparsityTranspose) {
353 // CreateJacobianBlockSparsityTranspose starts with a conservative
354 // estimate of the size of the sparsity pattern. This test ensures
355 // that when those estimates are violated, the reallocation/resizing
356 // logic works correctly.
361 vector<double*> parameter_blocks;
362 for (int i = 0; i < 20; ++i) {
363 problem.AddParameterBlock(x + i, 1);
364 parameter_blocks.push_back(x + i);
367 problem.AddResidualBlock(new NumParameterBlocksCostFunction<1, 20>(),
371 TripletSparseMatrix expected_block_sparse_jacobian(20, 1, 20);
373 int* rows = expected_block_sparse_jacobian.mutable_rows();
374 int* cols = expected_block_sparse_jacobian.mutable_cols();
375 for (int i = 0; i < 20; ++i) {
380 double* values = expected_block_sparse_jacobian.mutable_values();
381 std::fill(values, values + 20, 1.0);
382 expected_block_sparse_jacobian.set_num_nonzeros(20);
385 Program* program = problem.mutable_program();
386 program->SetParameterOffsetsAndIndex();
388 scoped_ptr<TripletSparseMatrix> actual_block_sparse_jacobian(
389 program->CreateJacobianBlockSparsityTranspose());
391 Matrix expected_dense_jacobian;
392 expected_block_sparse_jacobian.ToDenseMatrix(&expected_dense_jacobian);
394 Matrix actual_dense_jacobian;
395 actual_block_sparse_jacobian->ToDenseMatrix(&actual_dense_jacobian);
396 EXPECT_EQ((expected_dense_jacobian - actual_dense_jacobian).norm(), 0.0);
399 TEST(Program, ProblemHasNanParameterBlocks) {
403 x[1] = std::numeric_limits<double>::quiet_NaN();
404 problem.AddResidualBlock(new MockCostFunctionBase<1, 2, 0, 0>(), NULL, x);
406 EXPECT_FALSE(problem.program().ParameterBlocksAreFinite(&error));
407 EXPECT_NE(error.find("has at least one invalid value"),
408 string::npos) << error;
411 TEST(Program, InfeasibleParameterBlock) {
413 double x[] = {0.0, 0.0};
414 problem.AddResidualBlock(new MockCostFunctionBase<1, 2, 0, 0>(), NULL, x);
415 problem.SetParameterLowerBound(x, 0, 2.0);
416 problem.SetParameterUpperBound(x, 0, 1.0);
418 EXPECT_FALSE(problem.program().IsFeasible(&error));
419 EXPECT_NE(error.find("infeasible bound"), string::npos) << error;
422 TEST(Program, InfeasibleConstantParameterBlock) {
424 double x[] = {0.0, 0.0};
425 problem.AddResidualBlock(new MockCostFunctionBase<1, 2, 0, 0>(), NULL, x);
426 problem.SetParameterLowerBound(x, 0, 1.0);
427 problem.SetParameterUpperBound(x, 0, 2.0);
428 problem.SetParameterBlockConstant(x);
430 EXPECT_FALSE(problem.program().IsFeasible(&error));
431 EXPECT_NE(error.find("infeasible value"), string::npos) << error;
434 } // namespace internal