blob: 5eb6c66bfe4f0f6619587a1ac2af9b7ad6bfa40f [file] [log] [blame]
Keir Mierle8ebb0732012-04-30 23:09:08 -07001// Ceres Solver - A fast non-linear least squares minimizer
2// Copyright 2010, 2011, 2012 Google Inc. All rights reserved.
3// http://code.google.com/p/ceres-solver/
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 "gtest/gtest.h"
Keir Mierlef7471832012-06-14 11:31:53 -070032#include "ceres/autodiff_cost_function.h"
Keir Mierle8ebb0732012-04-30 23:09:08 -070033#include "ceres/linear_solver.h"
Sameer Agarwal2c94eed2012-10-01 16:34:37 -070034#include "ceres/ordered_groups.h"
Keir Mierle8ebb0732012-04-30 23:09:08 -070035#include "ceres/parameter_block.h"
36#include "ceres/problem_impl.h"
37#include "ceres/program.h"
38#include "ceres/residual_block.h"
39#include "ceres/solver_impl.h"
40#include "ceres/sized_cost_function.h"
41
Keir Mierle8ebb0732012-04-30 23:09:08 -070042namespace ceres {
43namespace internal {
44
Markus Moll8de78db2012-07-14 15:49:11 +020045// A cost function that sipmply returns its argument.
46class UnaryIdentityCostFunction : public SizedCostFunction<1, 1> {
47 public:
48 virtual bool Evaluate(double const* const* parameters,
49 double* residuals,
50 double** jacobians) const {
51 residuals[0] = parameters[0][0];
52 if (jacobians != NULL && jacobians[0] != NULL) {
53 jacobians[0][0] = 1.0;
54 }
55 return true;
56 }
57};
58
Keir Mierle8ebb0732012-04-30 23:09:08 -070059// Templated base class for the CostFunction signatures.
60template <int kNumResiduals, int N0, int N1, int N2>
61class MockCostFunctionBase : public
62SizedCostFunction<kNumResiduals, N0, N1, N2> {
63 public:
64 virtual bool Evaluate(double const* const* parameters,
65 double* residuals,
66 double** jacobians) const {
67 // Do nothing. This is never called.
68 return true;
69 }
70};
71
72class UnaryCostFunction : public MockCostFunctionBase<2, 1, 0, 0> {};
73class BinaryCostFunction : public MockCostFunctionBase<2, 1, 1, 0> {};
74class TernaryCostFunction : public MockCostFunctionBase<2, 1, 1, 1> {};
75
76TEST(SolverImpl, RemoveFixedBlocksNothingConstant) {
77 ProblemImpl problem;
78 double x;
79 double y;
80 double z;
81
82 problem.AddParameterBlock(&x, 1);
83 problem.AddParameterBlock(&y, 1);
84 problem.AddParameterBlock(&z, 1);
85 problem.AddResidualBlock(new UnaryCostFunction(), NULL, &x);
86 problem.AddResidualBlock(new BinaryCostFunction(), NULL, &x, &y);
87 problem.AddResidualBlock(new TernaryCostFunction(), NULL, &x, &y, &z);
88
89 string error;
90 {
Sameer Agarwal2c94eed2012-10-01 16:34:37 -070091 ParameterBlockOrdering ordering;
92 ordering.AddElementToGroup(&x, 0);
93 ordering.AddElementToGroup(&y, 0);
94 ordering.AddElementToGroup(&z, 0);
Sameer Agarwal65625f72012-09-17 12:06:57 -070095
Keir Mierle8ebb0732012-04-30 23:09:08 -070096 Program program(*problem.mutable_program());
97 EXPECT_TRUE(SolverImpl::RemoveFixedBlocksFromProgram(&program,
Sameer Agarwal65625f72012-09-17 12:06:57 -070098 &ordering,
Markus Moll8de78db2012-07-14 15:49:11 +020099 NULL,
Keir Mierle8ebb0732012-04-30 23:09:08 -0700100 &error));
101 EXPECT_EQ(program.NumParameterBlocks(), 3);
102 EXPECT_EQ(program.NumResidualBlocks(), 3);
Sameer Agarwal2c94eed2012-10-01 16:34:37 -0700103 EXPECT_EQ(ordering.NumElements(), 3);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700104 }
105}
106
107TEST(SolverImpl, RemoveFixedBlocksAllParameterBlocksConstant) {
108 ProblemImpl problem;
109 double x;
110
111 problem.AddParameterBlock(&x, 1);
112 problem.AddResidualBlock(new UnaryCostFunction(), NULL, &x);
113 problem.SetParameterBlockConstant(&x);
114
Sameer Agarwal2c94eed2012-10-01 16:34:37 -0700115 ParameterBlockOrdering ordering;
116 ordering.AddElementToGroup(&x, 0);
Sameer Agarwal65625f72012-09-17 12:06:57 -0700117
Keir Mierle8ebb0732012-04-30 23:09:08 -0700118 Program program(problem.program());
119 string error;
120 EXPECT_TRUE(SolverImpl::RemoveFixedBlocksFromProgram(&program,
Sameer Agarwal65625f72012-09-17 12:06:57 -0700121 &ordering,
Markus Moll8de78db2012-07-14 15:49:11 +0200122 NULL,
Keir Mierle8ebb0732012-04-30 23:09:08 -0700123 &error));
124 EXPECT_EQ(program.NumParameterBlocks(), 0);
125 EXPECT_EQ(program.NumResidualBlocks(), 0);
Sameer Agarwal2c94eed2012-10-01 16:34:37 -0700126 EXPECT_EQ(ordering.NumElements(), 0);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700127}
128
129TEST(SolverImpl, RemoveFixedBlocksNoResidualBlocks) {
130 ProblemImpl problem;
131 double x;
132 double y;
133 double z;
134
135 problem.AddParameterBlock(&x, 1);
136 problem.AddParameterBlock(&y, 1);
137 problem.AddParameterBlock(&z, 1);
138
Sameer Agarwal2c94eed2012-10-01 16:34:37 -0700139 ParameterBlockOrdering ordering;
140 ordering.AddElementToGroup(&x, 0);
141 ordering.AddElementToGroup(&y, 0);
142 ordering.AddElementToGroup(&z, 0);
Sameer Agarwal65625f72012-09-17 12:06:57 -0700143
144
Keir Mierle8ebb0732012-04-30 23:09:08 -0700145 Program program(problem.program());
146 string error;
147 EXPECT_TRUE(SolverImpl::RemoveFixedBlocksFromProgram(&program,
Sameer Agarwal65625f72012-09-17 12:06:57 -0700148 &ordering,
Markus Moll8de78db2012-07-14 15:49:11 +0200149 NULL,
Keir Mierle8ebb0732012-04-30 23:09:08 -0700150 &error));
151 EXPECT_EQ(program.NumParameterBlocks(), 0);
152 EXPECT_EQ(program.NumResidualBlocks(), 0);
Sameer Agarwal2c94eed2012-10-01 16:34:37 -0700153 EXPECT_EQ(ordering.NumElements(), 0);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700154}
155
156TEST(SolverImpl, RemoveFixedBlocksOneParameterBlockConstant) {
157 ProblemImpl problem;
158 double x;
159 double y;
160 double z;
161
162 problem.AddParameterBlock(&x, 1);
163 problem.AddParameterBlock(&y, 1);
164 problem.AddParameterBlock(&z, 1);
Sameer Agarwal65625f72012-09-17 12:06:57 -0700165
Sameer Agarwal2c94eed2012-10-01 16:34:37 -0700166 ParameterBlockOrdering ordering;
167 ordering.AddElementToGroup(&x, 0);
168 ordering.AddElementToGroup(&y, 0);
169 ordering.AddElementToGroup(&z, 0);
Sameer Agarwal65625f72012-09-17 12:06:57 -0700170
Keir Mierle8ebb0732012-04-30 23:09:08 -0700171 problem.AddResidualBlock(new UnaryCostFunction(), NULL, &x);
172 problem.AddResidualBlock(new BinaryCostFunction(), NULL, &x, &y);
173 problem.SetParameterBlockConstant(&x);
174
Sameer Agarwal65625f72012-09-17 12:06:57 -0700175
Keir Mierle8ebb0732012-04-30 23:09:08 -0700176 Program program(problem.program());
177 string error;
178 EXPECT_TRUE(SolverImpl::RemoveFixedBlocksFromProgram(&program,
Sameer Agarwal65625f72012-09-17 12:06:57 -0700179 &ordering,
Markus Moll8de78db2012-07-14 15:49:11 +0200180 NULL,
Keir Mierle8ebb0732012-04-30 23:09:08 -0700181 &error));
182 EXPECT_EQ(program.NumParameterBlocks(), 1);
183 EXPECT_EQ(program.NumResidualBlocks(), 1);
Sameer Agarwal2c94eed2012-10-01 16:34:37 -0700184 EXPECT_EQ(ordering.NumElements(), 1);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700185}
186
187TEST(SolverImpl, RemoveFixedBlocksNumEliminateBlocks) {
188 ProblemImpl problem;
189 double x;
190 double y;
191 double z;
192
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);
200
Sameer Agarwal2c94eed2012-10-01 16:34:37 -0700201 ParameterBlockOrdering ordering;
202 ordering.AddElementToGroup(&x, 0);
203 ordering.AddElementToGroup(&y, 0);
204 ordering.AddElementToGroup(&z, 1);
Sameer Agarwal65625f72012-09-17 12:06:57 -0700205
Keir Mierle8ebb0732012-04-30 23:09:08 -0700206 Program program(problem.program());
207 string error;
208 EXPECT_TRUE(SolverImpl::RemoveFixedBlocksFromProgram(&program,
Sameer Agarwal65625f72012-09-17 12:06:57 -0700209 &ordering,
Markus Moll8de78db2012-07-14 15:49:11 +0200210 NULL,
Keir Mierle8ebb0732012-04-30 23:09:08 -0700211 &error));
212 EXPECT_EQ(program.NumParameterBlocks(), 2);
213 EXPECT_EQ(program.NumResidualBlocks(), 2);
Sameer Agarwal2c94eed2012-10-01 16:34:37 -0700214 EXPECT_EQ(ordering.NumElements(), 2);
215 EXPECT_EQ(ordering.GroupId(&y), 0);
216 EXPECT_EQ(ordering.GroupId(&z), 1);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700217}
218
Markus Moll8de78db2012-07-14 15:49:11 +0200219TEST(SolverImpl, RemoveFixedBlocksFixedCost) {
220 ProblemImpl problem;
221 double x = 1.23;
222 double y = 4.56;
223 double z = 7.89;
224
225 problem.AddParameterBlock(&x, 1);
226 problem.AddParameterBlock(&y, 1);
227 problem.AddParameterBlock(&z, 1);
228 problem.AddResidualBlock(new UnaryIdentityCostFunction(), NULL, &x);
229 problem.AddResidualBlock(new TernaryCostFunction(), NULL, &x, &y, &z);
230 problem.AddResidualBlock(new BinaryCostFunction(), NULL, &x, &y);
231 problem.SetParameterBlockConstant(&x);
232
Sameer Agarwal2c94eed2012-10-01 16:34:37 -0700233 ParameterBlockOrdering ordering;
234 ordering.AddElementToGroup(&x, 0);
235 ordering.AddElementToGroup(&y, 0);
236 ordering.AddElementToGroup(&z, 1);
Sameer Agarwal65625f72012-09-17 12:06:57 -0700237
Markus Moll8de78db2012-07-14 15:49:11 +0200238 double fixed_cost = 0.0;
239 Program program(problem.program());
240
241 double expected_fixed_cost;
242 ResidualBlock *expected_removed_block = program.residual_blocks()[0];
243 scoped_array<double> scratch(new double[expected_removed_block->NumScratchDoublesForEvaluate()]);
244 expected_removed_block->Evaluate(&expected_fixed_cost, NULL, NULL, scratch.get());
245
246 string error;
247 EXPECT_TRUE(SolverImpl::RemoveFixedBlocksFromProgram(&program,
Sameer Agarwal65625f72012-09-17 12:06:57 -0700248 &ordering,
Markus Moll8de78db2012-07-14 15:49:11 +0200249 &fixed_cost,
250 &error));
251 EXPECT_EQ(program.NumParameterBlocks(), 2);
252 EXPECT_EQ(program.NumResidualBlocks(), 2);
Sameer Agarwal2c94eed2012-10-01 16:34:37 -0700253 EXPECT_EQ(ordering.NumElements(), 2);
254 EXPECT_EQ(ordering.GroupId(&y), 0);
255 EXPECT_EQ(ordering.GroupId(&z), 1);
Markus Moll8de78db2012-07-14 15:49:11 +0200256 EXPECT_DOUBLE_EQ(fixed_cost, expected_fixed_cost);
257}
258
Keir Mierle8ebb0732012-04-30 23:09:08 -0700259TEST(SolverImpl, ReorderResidualBlockNormalFunction) {
260 ProblemImpl problem;
261 double x;
262 double y;
263 double z;
264
265 problem.AddParameterBlock(&x, 1);
266 problem.AddParameterBlock(&y, 1);
267 problem.AddParameterBlock(&z, 1);
268
269 problem.AddResidualBlock(new UnaryCostFunction(), NULL, &x);
270 problem.AddResidualBlock(new BinaryCostFunction(), NULL, &z, &x);
271 problem.AddResidualBlock(new BinaryCostFunction(), NULL, &z, &y);
272 problem.AddResidualBlock(new UnaryCostFunction(), NULL, &z);
273 problem.AddResidualBlock(new BinaryCostFunction(), NULL, &x, &y);
274 problem.AddResidualBlock(new UnaryCostFunction(), NULL, &y);
275
Sameer Agarwal2c94eed2012-10-01 16:34:37 -0700276 ParameterBlockOrdering* ordering = new ParameterBlockOrdering;
277 ordering->AddElementToGroup(&x, 0);
278 ordering->AddElementToGroup(&y, 0);
279 ordering->AddElementToGroup(&z, 1);
Sameer Agarwal65625f72012-09-17 12:06:57 -0700280
Keir Mierle8ebb0732012-04-30 23:09:08 -0700281 Solver::Options options;
282 options.linear_solver_type = DENSE_SCHUR;
Sameer Agarwal68b32a92012-10-06 23:10:51 -0700283 options.linear_solver_ordering = ordering;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700284
285 const vector<ResidualBlock*>& residual_blocks =
286 problem.program().residual_blocks();
287
288 vector<ResidualBlock*> expected_residual_blocks;
289
290 // This is a bit fragile, but it serves the purpose. We know the
291 // bucketing algorithm that the reordering function uses, so we
292 // expect the order for residual blocks for each e_block to be
293 // filled in reverse.
294 expected_residual_blocks.push_back(residual_blocks[4]);
295 expected_residual_blocks.push_back(residual_blocks[1]);
296 expected_residual_blocks.push_back(residual_blocks[0]);
297 expected_residual_blocks.push_back(residual_blocks[5]);
298 expected_residual_blocks.push_back(residual_blocks[2]);
299 expected_residual_blocks.push_back(residual_blocks[3]);
300
301 Program* program = problem.mutable_program();
302 program->SetParameterOffsetsAndIndex();
303
304 string error;
Sameer Agarwal9123e2f2012-09-18 21:49:06 -0700305 EXPECT_TRUE(SolverImpl::LexicographicallyOrderResidualBlocks(
306 2,
307 problem.mutable_program(),
308 &error));
Keir Mierle32de18d2012-05-13 16:45:05 -0700309 EXPECT_EQ(residual_blocks.size(), expected_residual_blocks.size());
Keir Mierle8ebb0732012-04-30 23:09:08 -0700310 for (int i = 0; i < expected_residual_blocks.size(); ++i) {
311 EXPECT_EQ(residual_blocks[i], expected_residual_blocks[i]);
312 }
313}
314
Keir Mierle32de18d2012-05-13 16:45:05 -0700315TEST(SolverImpl, ReorderResidualBlockNormalFunctionWithFixedBlocks) {
316 ProblemImpl problem;
317 double x;
318 double y;
319 double z;
320
321 problem.AddParameterBlock(&x, 1);
322 problem.AddParameterBlock(&y, 1);
323 problem.AddParameterBlock(&z, 1);
324
325 // Set one parameter block constant.
326 problem.SetParameterBlockConstant(&z);
327
328 // Mark residuals for x's row block with "x" for readability.
329 problem.AddResidualBlock(new UnaryCostFunction(), NULL, &x); // 0 x
330 problem.AddResidualBlock(new BinaryCostFunction(), NULL, &z, &x); // 1 x
331 problem.AddResidualBlock(new BinaryCostFunction(), NULL, &z, &y); // 2
332 problem.AddResidualBlock(new BinaryCostFunction(), NULL, &z, &y); // 3
333 problem.AddResidualBlock(new BinaryCostFunction(), NULL, &x, &z); // 4 x
334 problem.AddResidualBlock(new BinaryCostFunction(), NULL, &z, &y); // 5
335 problem.AddResidualBlock(new BinaryCostFunction(), NULL, &x, &z); // 6 x
336 problem.AddResidualBlock(new UnaryCostFunction(), NULL, &y); // 7
337
Sameer Agarwal2c94eed2012-10-01 16:34:37 -0700338 ParameterBlockOrdering* ordering = new ParameterBlockOrdering;
339 ordering->AddElementToGroup(&x, 0);
340 ordering->AddElementToGroup(&z, 0);
341 ordering->AddElementToGroup(&y, 1);
Sameer Agarwal65625f72012-09-17 12:06:57 -0700342
Keir Mierle32de18d2012-05-13 16:45:05 -0700343 Solver::Options options;
344 options.linear_solver_type = DENSE_SCHUR;
Sameer Agarwal68b32a92012-10-06 23:10:51 -0700345 options.linear_solver_ordering = ordering;
Keir Mierle32de18d2012-05-13 16:45:05 -0700346
347 // Create the reduced program. This should remove the fixed block "z",
348 // marking the index to -1 at the same time. x and y also get indices.
349 string error;
350 scoped_ptr<Program> reduced_program(
Markus Moll8de78db2012-07-14 15:49:11 +0200351 SolverImpl::CreateReducedProgram(&options, &problem, NULL, &error));
Keir Mierle32de18d2012-05-13 16:45:05 -0700352
353 const vector<ResidualBlock*>& residual_blocks =
354 problem.program().residual_blocks();
355
356 // This is a bit fragile, but it serves the purpose. We know the
357 // bucketing algorithm that the reordering function uses, so we
358 // expect the order for residual blocks for each e_block to be
359 // filled in reverse.
360
361 vector<ResidualBlock*> expected_residual_blocks;
362
363 // Row block for residuals involving "x". These are marked "x" in the block
364 // of code calling AddResidual() above.
365 expected_residual_blocks.push_back(residual_blocks[6]);
366 expected_residual_blocks.push_back(residual_blocks[4]);
367 expected_residual_blocks.push_back(residual_blocks[1]);
368 expected_residual_blocks.push_back(residual_blocks[0]);
369
370 // Row block for residuals involving "y".
371 expected_residual_blocks.push_back(residual_blocks[7]);
372 expected_residual_blocks.push_back(residual_blocks[5]);
373 expected_residual_blocks.push_back(residual_blocks[3]);
374 expected_residual_blocks.push_back(residual_blocks[2]);
375
Sameer Agarwal9123e2f2012-09-18 21:49:06 -0700376 EXPECT_TRUE(SolverImpl::LexicographicallyOrderResidualBlocks(
377 2,
378 reduced_program.get(),
379 &error));
Keir Mierle32de18d2012-05-13 16:45:05 -0700380
381 EXPECT_EQ(reduced_program->residual_blocks().size(),
382 expected_residual_blocks.size());
383 for (int i = 0; i < expected_residual_blocks.size(); ++i) {
384 EXPECT_EQ(reduced_program->residual_blocks()[i],
385 expected_residual_blocks[i]);
386 }
387}
388
Sameer Agarwalc1ffad62012-10-05 16:29:37 -0700389TEST(SolverImpl, AutomaticSchurReorderingRespectsConstantBlocks) {
390 ProblemImpl problem;
391 double x;
392 double y;
393 double z;
394
395 problem.AddParameterBlock(&x, 1);
396 problem.AddParameterBlock(&y, 1);
397 problem.AddParameterBlock(&z, 1);
398
399 // Set one parameter block constant.
400 problem.SetParameterBlockConstant(&z);
401
402 problem.AddResidualBlock(new UnaryCostFunction(), NULL, &x);
403 problem.AddResidualBlock(new BinaryCostFunction(), NULL, &z, &x);
404 problem.AddResidualBlock(new BinaryCostFunction(), NULL, &z, &y);
405 problem.AddResidualBlock(new BinaryCostFunction(), NULL, &z, &y);
406 problem.AddResidualBlock(new BinaryCostFunction(), NULL, &x, &z);
407 problem.AddResidualBlock(new BinaryCostFunction(), NULL, &z, &y);
408 problem.AddResidualBlock(new BinaryCostFunction(), NULL, &x, &z);
409 problem.AddResidualBlock(new UnaryCostFunction(), NULL, &y);
410 problem.AddResidualBlock(new UnaryCostFunction(), NULL, &z);
411
412 ParameterBlockOrdering* ordering = new ParameterBlockOrdering;
413 ordering->AddElementToGroup(&x, 0);
414 ordering->AddElementToGroup(&z, 0);
415 ordering->AddElementToGroup(&y, 0);
416
417 Solver::Options options;
418 options.linear_solver_type = DENSE_SCHUR;
Sameer Agarwal68b32a92012-10-06 23:10:51 -0700419 options.linear_solver_ordering = ordering;
Sameer Agarwalc1ffad62012-10-05 16:29:37 -0700420
421 string error;
422 scoped_ptr<Program> reduced_program(
423 SolverImpl::CreateReducedProgram(&options, &problem, NULL, &error));
424
425 const vector<ResidualBlock*>& residual_blocks =
426 reduced_program->residual_blocks();
427 const vector<ParameterBlock*>& parameter_blocks =
428 reduced_program->parameter_blocks();
429
430 const vector<ResidualBlock*>& original_residual_blocks =
431 problem.program().residual_blocks();
432
433 EXPECT_EQ(residual_blocks.size(), 8);
434 EXPECT_EQ(reduced_program->parameter_blocks().size(), 2);
435
436 // Verify that right parmeter block and the residual blocks have
437 // been removed.
438 for (int i = 0; i < 8; ++i) {
439 EXPECT_NE(residual_blocks[i], original_residual_blocks.back());
440 }
441 for (int i = 0; i < 2; ++i) {
442 EXPECT_NE(parameter_blocks[i]->mutable_user_state(), &z);
443 }
444}
445
Keir Mierle8ebb0732012-04-30 23:09:08 -0700446TEST(SolverImpl, ApplyUserOrderingOrderingTooSmall) {
447 ProblemImpl problem;
448 double x;
449 double y;
450 double z;
451
452 problem.AddParameterBlock(&x, 1);
453 problem.AddParameterBlock(&y, 1);
454 problem.AddParameterBlock(&z, 1);
455
Sameer Agarwal2c94eed2012-10-01 16:34:37 -0700456 ParameterBlockOrdering ordering;
457 ordering.AddElementToGroup(&x, 0);
458 ordering.AddElementToGroup(&y, 1);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700459
460 Program program(problem.program());
461 string error;
Sameer Agarwal9123e2f2012-09-18 21:49:06 -0700462 EXPECT_FALSE(SolverImpl::ApplyUserOrdering(problem.parameter_map(),
Sameer Agarwal65625f72012-09-17 12:06:57 -0700463 &ordering,
Keir Mierle8ebb0732012-04-30 23:09:08 -0700464 &program,
465 &error));
466}
467
Keir Mierle8ebb0732012-04-30 23:09:08 -0700468TEST(SolverImpl, ApplyUserOrderingNormal) {
469 ProblemImpl problem;
470 double x;
471 double y;
472 double z;
473
474 problem.AddParameterBlock(&x, 1);
475 problem.AddParameterBlock(&y, 1);
476 problem.AddParameterBlock(&z, 1);
477
Sameer Agarwal2c94eed2012-10-01 16:34:37 -0700478 ParameterBlockOrdering ordering;
479 ordering.AddElementToGroup(&x, 0);
480 ordering.AddElementToGroup(&y, 2);
481 ordering.AddElementToGroup(&z, 1);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700482
483 Program* program = problem.mutable_program();
484 string error;
485
Sameer Agarwal9123e2f2012-09-18 21:49:06 -0700486 EXPECT_TRUE(SolverImpl::ApplyUserOrdering(problem.parameter_map(),
Sameer Agarwal65625f72012-09-17 12:06:57 -0700487 &ordering,
Keir Mierle8ebb0732012-04-30 23:09:08 -0700488 program,
489 &error));
490 const vector<ParameterBlock*>& parameter_blocks = program->parameter_blocks();
491
492 EXPECT_EQ(parameter_blocks.size(), 3);
493 EXPECT_EQ(parameter_blocks[0]->user_state(), &x);
494 EXPECT_EQ(parameter_blocks[1]->user_state(), &z);
495 EXPECT_EQ(parameter_blocks[2]->user_state(), &y);
496}
497
Sameer Agarwalb0518732012-05-29 00:27:57 -0700498#if defined(CERES_NO_SUITESPARSE) && defined(CERES_NO_CXSPARSE)
Keir Mierle8ebb0732012-04-30 23:09:08 -0700499TEST(SolverImpl, CreateLinearSolverNoSuiteSparse) {
500 Solver::Options options;
501 options.linear_solver_type = SPARSE_NORMAL_CHOLESKY;
502 string error;
503 EXPECT_FALSE(SolverImpl::CreateLinearSolver(&options, &error));
504}
Sameer Agarwalb0518732012-05-29 00:27:57 -0700505#endif
Keir Mierle8ebb0732012-04-30 23:09:08 -0700506
507TEST(SolverImpl, CreateLinearSolverNegativeMaxNumIterations) {
508 Solver::Options options;
509 options.linear_solver_type = DENSE_QR;
510 options.linear_solver_max_num_iterations = -1;
Sameer Agarwal65625f72012-09-17 12:06:57 -0700511 // CreateLinearSolver assumes a non-empty ordering.
Sameer Agarwal68b32a92012-10-06 23:10:51 -0700512 options.linear_solver_ordering = new ParameterBlockOrdering;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700513 string error;
514 EXPECT_EQ(SolverImpl::CreateLinearSolver(&options, &error),
515 static_cast<LinearSolver*>(NULL));
516}
517
518TEST(SolverImpl, CreateLinearSolverNegativeMinNumIterations) {
519 Solver::Options options;
520 options.linear_solver_type = DENSE_QR;
521 options.linear_solver_min_num_iterations = -1;
Sameer Agarwal65625f72012-09-17 12:06:57 -0700522 // CreateLinearSolver assumes a non-empty ordering.
Sameer Agarwal68b32a92012-10-06 23:10:51 -0700523 options.linear_solver_ordering = new ParameterBlockOrdering;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700524 string error;
525 EXPECT_EQ(SolverImpl::CreateLinearSolver(&options, &error),
526 static_cast<LinearSolver*>(NULL));
527}
528
529TEST(SolverImpl, CreateLinearSolverMaxLessThanMinIterations) {
530 Solver::Options options;
531 options.linear_solver_type = DENSE_QR;
532 options.linear_solver_min_num_iterations = 10;
533 options.linear_solver_max_num_iterations = 5;
Sameer Agarwal68b32a92012-10-06 23:10:51 -0700534 options.linear_solver_ordering = new ParameterBlockOrdering;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700535 string error;
536 EXPECT_EQ(SolverImpl::CreateLinearSolver(&options, &error),
537 static_cast<LinearSolver*>(NULL));
538}
539
Keir Mierle8ebb0732012-04-30 23:09:08 -0700540TEST(SolverImpl, CreateLinearSolverDenseSchurMultipleThreads) {
541 Solver::Options options;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700542 options.linear_solver_type = DENSE_SCHUR;
543 options.num_linear_solver_threads = 2;
Sameer Agarwal65625f72012-09-17 12:06:57 -0700544 // The Schur type solvers can only be created with the Ordering
545 // contains at least one elimination group.
Sameer Agarwal68b32a92012-10-06 23:10:51 -0700546 options.linear_solver_ordering = new ParameterBlockOrdering;
Sameer Agarwal65625f72012-09-17 12:06:57 -0700547 double x;
548 double y;
Sameer Agarwal68b32a92012-10-06 23:10:51 -0700549 options.linear_solver_ordering->AddElementToGroup(&x, 0);
550 options.linear_solver_ordering->AddElementToGroup(&y, 0);
Sameer Agarwal65625f72012-09-17 12:06:57 -0700551
Keir Mierle8ebb0732012-04-30 23:09:08 -0700552 string error;
553 scoped_ptr<LinearSolver> solver(
554 SolverImpl::CreateLinearSolver(&options, &error));
555 EXPECT_TRUE(solver != NULL);
556 EXPECT_EQ(options.linear_solver_type, DENSE_SCHUR);
557 EXPECT_EQ(options.num_linear_solver_threads, 1);
558}
559
Sameer Agarwal5ecd2512012-06-17 16:34:03 -0700560TEST(SolverImpl, CreateIterativeLinearSolverForDogleg) {
561 Solver::Options options;
562 options.trust_region_strategy_type = DOGLEG;
Sameer Agarwal65625f72012-09-17 12:06:57 -0700563 // CreateLinearSolver assumes a non-empty ordering.
Sameer Agarwal68b32a92012-10-06 23:10:51 -0700564 options.linear_solver_ordering = new ParameterBlockOrdering;
Sameer Agarwal5ecd2512012-06-17 16:34:03 -0700565 string error;
566 options.linear_solver_type = ITERATIVE_SCHUR;
567 EXPECT_EQ(SolverImpl::CreateLinearSolver(&options, &error),
568 static_cast<LinearSolver*>(NULL));
569
570 options.linear_solver_type = CGNR;
571 EXPECT_EQ(SolverImpl::CreateLinearSolver(&options, &error),
572 static_cast<LinearSolver*>(NULL));
573}
574
Keir Mierle8ebb0732012-04-30 23:09:08 -0700575TEST(SolverImpl, CreateLinearSolverNormalOperation) {
576 Solver::Options options;
577 scoped_ptr<LinearSolver> solver;
578 options.linear_solver_type = DENSE_QR;
Sameer Agarwal65625f72012-09-17 12:06:57 -0700579 // CreateLinearSolver assumes a non-empty ordering.
Sameer Agarwal68b32a92012-10-06 23:10:51 -0700580 options.linear_solver_ordering = new ParameterBlockOrdering;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700581 string error;
582 solver.reset(SolverImpl::CreateLinearSolver(&options, &error));
583 EXPECT_EQ(options.linear_solver_type, DENSE_QR);
584 EXPECT_TRUE(solver.get() != NULL);
585
Sameer Agarwalb9f15a52012-08-18 13:06:19 -0700586 options.linear_solver_type = DENSE_NORMAL_CHOLESKY;
587 solver.reset(SolverImpl::CreateLinearSolver(&options, &error));
588 EXPECT_EQ(options.linear_solver_type, DENSE_NORMAL_CHOLESKY);
589 EXPECT_TRUE(solver.get() != NULL);
590
Keir Mierle8ebb0732012-04-30 23:09:08 -0700591#ifndef CERES_NO_SUITESPARSE
592 options.linear_solver_type = SPARSE_NORMAL_CHOLESKY;
Sameer Agarwalb0518732012-05-29 00:27:57 -0700593 options.sparse_linear_algebra_library = SUITE_SPARSE;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700594 solver.reset(SolverImpl::CreateLinearSolver(&options, &error));
595 EXPECT_EQ(options.linear_solver_type, SPARSE_NORMAL_CHOLESKY);
596 EXPECT_TRUE(solver.get() != NULL);
Sameer Agarwalb0518732012-05-29 00:27:57 -0700597#endif
598
599#ifndef CERES_NO_CXSPARSE
600 options.linear_solver_type = SPARSE_NORMAL_CHOLESKY;
601 options.sparse_linear_algebra_library = CX_SPARSE;
602 solver.reset(SolverImpl::CreateLinearSolver(&options, &error));
603 EXPECT_EQ(options.linear_solver_type, SPARSE_NORMAL_CHOLESKY);
604 EXPECT_TRUE(solver.get() != NULL);
605#endif
Keir Mierle8ebb0732012-04-30 23:09:08 -0700606
Sameer Agarwal65625f72012-09-17 12:06:57 -0700607 double x;
608 double y;
Sameer Agarwal68b32a92012-10-06 23:10:51 -0700609 options.linear_solver_ordering->AddElementToGroup(&x, 0);
610 options.linear_solver_ordering->AddElementToGroup(&y, 0);
Sameer Agarwal65625f72012-09-17 12:06:57 -0700611
Keir Mierle8ebb0732012-04-30 23:09:08 -0700612 options.linear_solver_type = DENSE_SCHUR;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700613 solver.reset(SolverImpl::CreateLinearSolver(&options, &error));
614 EXPECT_EQ(options.linear_solver_type, DENSE_SCHUR);
615 EXPECT_TRUE(solver.get() != NULL);
616
617 options.linear_solver_type = SPARSE_SCHUR;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700618 solver.reset(SolverImpl::CreateLinearSolver(&options, &error));
Sameer Agarwalb0518732012-05-29 00:27:57 -0700619
620#if defined(CERES_NO_SUITESPARSE) && defined(CERES_NO_CXSPARSE)
621 EXPECT_TRUE(SolverImpl::CreateLinearSolver(&options, &error) == NULL);
622#else
Keir Mierle8ebb0732012-04-30 23:09:08 -0700623 EXPECT_TRUE(solver.get() != NULL);
624 EXPECT_EQ(options.linear_solver_type, SPARSE_SCHUR);
Sameer Agarwalb0518732012-05-29 00:27:57 -0700625#endif
Keir Mierle8ebb0732012-04-30 23:09:08 -0700626
627 options.linear_solver_type = ITERATIVE_SCHUR;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700628 solver.reset(SolverImpl::CreateLinearSolver(&options, &error));
629 EXPECT_EQ(options.linear_solver_type, ITERATIVE_SCHUR);
630 EXPECT_TRUE(solver.get() != NULL);
631}
632
Keir Mierlef7471832012-06-14 11:31:53 -0700633struct QuadraticCostFunction {
634 template <typename T> bool operator()(const T* const x,
635 T* residual) const {
636 residual[0] = T(5.0) - *x;
637 return true;
638 }
639};
640
641struct RememberingCallback : public IterationCallback {
Sameer Agarwal4997cbc2012-07-02 12:44:34 -0700642 explicit RememberingCallback(double *x) : calls(0), x(x) {}
Keir Mierlef7471832012-06-14 11:31:53 -0700643 virtual ~RememberingCallback() {}
644 virtual CallbackReturnType operator()(const IterationSummary& summary) {
645 x_values.push_back(*x);
646 return SOLVER_CONTINUE;
647 }
648 int calls;
649 double *x;
650 vector<double> x_values;
651};
652
653TEST(SolverImpl, UpdateStateEveryIterationOption) {
654 double x = 50.0;
655 const double original_x = x;
656
657 scoped_ptr<CostFunction> cost_function(
658 new AutoDiffCostFunction<QuadraticCostFunction, 1, 1>(
659 new QuadraticCostFunction));
660
661 Problem::Options problem_options;
662 problem_options.cost_function_ownership = DO_NOT_TAKE_OWNERSHIP;
Keir Mierle6196cba2012-06-18 11:03:40 -0700663 ProblemImpl problem(problem_options);
Keir Mierlef7471832012-06-14 11:31:53 -0700664 problem.AddResidualBlock(cost_function.get(), NULL, &x);
665
666 Solver::Options options;
667 options.linear_solver_type = DENSE_QR;
668
669 RememberingCallback callback(&x);
670 options.callbacks.push_back(&callback);
671
672 Solver::Summary summary;
673
674 int num_iterations;
675
676 // First try: no updating.
677 SolverImpl::Solve(options, &problem, &summary);
678 num_iterations = summary.num_successful_steps +
679 summary.num_unsuccessful_steps;
680 EXPECT_GT(num_iterations, 1);
681 for (int i = 0; i < callback.x_values.size(); ++i) {
682 EXPECT_EQ(50.0, callback.x_values[i]);
683 }
684
685 // Second try: with updating
686 x = 50.0;
687 options.update_state_every_iteration = true;
688 callback.x_values.clear();
689 SolverImpl::Solve(options, &problem, &summary);
690 num_iterations = summary.num_successful_steps +
691 summary.num_unsuccessful_steps;
692 EXPECT_GT(num_iterations, 1);
693 EXPECT_EQ(original_x, callback.x_values[0]);
694 EXPECT_NE(original_x, callback.x_values[1]);
695}
696
Keir Mierle6196cba2012-06-18 11:03:40 -0700697// The parameters must be in separate blocks so that they can be individually
698// set constant or not.
699struct Quadratic4DCostFunction {
700 template <typename T> bool operator()(const T* const x,
701 const T* const y,
702 const T* const z,
703 const T* const w,
704 T* residual) const {
705 // A 4-dimension axis-aligned quadratic.
706 residual[0] = T(10.0) - *x +
707 T(20.0) - *y +
708 T(30.0) - *z +
709 T(40.0) - *w;
710 return true;
711 }
712};
713
714TEST(SolverImpl, ConstantParameterBlocksDoNotChangeAndStateInvariantKept) {
715 double x = 50.0;
716 double y = 50.0;
717 double z = 50.0;
718 double w = 50.0;
719 const double original_x = 50.0;
720 const double original_y = 50.0;
721 const double original_z = 50.0;
722 const double original_w = 50.0;
723
724 scoped_ptr<CostFunction> cost_function(
725 new AutoDiffCostFunction<Quadratic4DCostFunction, 1, 1, 1, 1, 1>(
726 new Quadratic4DCostFunction));
727
728 Problem::Options problem_options;
729 problem_options.cost_function_ownership = DO_NOT_TAKE_OWNERSHIP;
730
731 ProblemImpl problem(problem_options);
732 problem.AddResidualBlock(cost_function.get(), NULL, &x, &y, &z, &w);
733 problem.SetParameterBlockConstant(&x);
734 problem.SetParameterBlockConstant(&w);
735
736 Solver::Options options;
737 options.linear_solver_type = DENSE_QR;
738
739 Solver::Summary summary;
740 SolverImpl::Solve(options, &problem, &summary);
741
742 // Verify only the non-constant parameters were mutated.
743 EXPECT_EQ(original_x, x);
744 EXPECT_NE(original_y, y);
745 EXPECT_NE(original_z, z);
746 EXPECT_EQ(original_w, w);
747
748 // Check that the parameter block state pointers are pointing back at the
749 // user state, instead of inside a random temporary vector made by Solve().
750 EXPECT_EQ(&x, problem.program().parameter_blocks()[0]->state());
751 EXPECT_EQ(&y, problem.program().parameter_blocks()[1]->state());
752 EXPECT_EQ(&z, problem.program().parameter_blocks()[2]->state());
753 EXPECT_EQ(&w, problem.program().parameter_blocks()[3]->state());
754}
755
Sameer Agarwal4997cbc2012-07-02 12:44:34 -0700756#define CHECK_ARRAY(name, value) \
757 if (options.return_ ## name) { \
758 EXPECT_EQ(summary.name.size(), 1); \
759 EXPECT_EQ(summary.name[0], value); \
760 } else { \
761 EXPECT_EQ(summary.name.size(), 0); \
762 }
763
764#define CHECK_JACOBIAN(name) \
765 if (options.return_ ## name) { \
766 EXPECT_EQ(summary.name.num_rows, 1); \
767 EXPECT_EQ(summary.name.num_cols, 1); \
768 EXPECT_EQ(summary.name.cols.size(), 2); \
769 EXPECT_EQ(summary.name.cols[0], 0); \
770 EXPECT_EQ(summary.name.cols[1], 1); \
771 EXPECT_EQ(summary.name.rows.size(), 1); \
772 EXPECT_EQ(summary.name.rows[0], 0); \
773 EXPECT_EQ(summary.name.values.size(), 0); \
774 EXPECT_EQ(summary.name.values[0], name); \
775 } else { \
776 EXPECT_EQ(summary.name.num_rows, 0); \
777 EXPECT_EQ(summary.name.num_cols, 0); \
778 EXPECT_EQ(summary.name.cols.size(), 0); \
779 EXPECT_EQ(summary.name.rows.size(), 0); \
780 EXPECT_EQ(summary.name.values.size(), 0); \
781 }
782
783void SolveAndCompare(const Solver::Options& options) {
784 ProblemImpl problem;
785 double x = 1.0;
786
787 const double initial_residual = 5.0 - x;
788 const double initial_jacobian = -1.0;
789 const double initial_gradient = initial_residual * initial_jacobian;
790
791 problem.AddResidualBlock(
792 new AutoDiffCostFunction<QuadraticCostFunction, 1, 1>(
793 new QuadraticCostFunction),
794 NULL,
795 &x);
796 Solver::Summary summary;
797 SolverImpl::Solve(options, &problem, &summary);
798
799 const double final_residual = 5.0 - x;
800 const double final_jacobian = -1.0;
801 const double final_gradient = final_residual * final_jacobian;
802
803 CHECK_ARRAY(initial_residuals, initial_residual);
804 CHECK_ARRAY(initial_gradient, initial_gradient);
805 CHECK_JACOBIAN(initial_jacobian);
806 CHECK_ARRAY(final_residuals, final_residual);
807 CHECK_ARRAY(final_gradient, final_gradient);
808 CHECK_JACOBIAN(initial_jacobian);
809}
810
811#undef CHECK_ARRAY
812#undef CHECK_JACOBIAN
813
814TEST(SolverImpl, InitialAndFinalResidualsGradientAndJacobian) {
815 for (int i = 0; i < 64; ++i) {
816 Solver::Options options;
817 options.return_initial_residuals = (i & 1);
818 options.return_initial_gradient = (i & 2);
819 options.return_initial_jacobian = (i & 4);
820 options.return_final_residuals = (i & 8);
821 options.return_final_gradient = (i & 16);
822 options.return_final_jacobian = (i & 64);
823 }
824}
825
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700826TEST(SolverImpl, NoParameterBlocks) {
827 ProblemImpl problem_impl;
828 Solver::Options options;
829 Solver::Summary summary;
830 SolverImpl::Solve(options, &problem_impl, &summary);
831 EXPECT_EQ(summary.termination_type, DID_NOT_RUN);
832 EXPECT_EQ(summary.error, "Problem contains no parameter blocks.");
833}
834
835TEST(SolverImpl, NoResiduals) {
836 ProblemImpl problem_impl;
837 Solver::Options options;
838 Solver::Summary summary;
839 double x = 1;
840 problem_impl.AddParameterBlock(&x, 1);
841 SolverImpl::Solve(options, &problem_impl, &summary);
842 EXPECT_EQ(summary.termination_type, DID_NOT_RUN);
843 EXPECT_EQ(summary.error, "Problem contains no residual blocks.");
844}
845
846class FailingCostFunction : public SizedCostFunction<1, 1> {
847 public:
848 virtual bool Evaluate(double const* const* parameters,
849 double* residuals,
850 double** jacobians) const {
851 return false;
852 }
853};
854
855TEST(SolverImpl, InitialCostEvaluationFails) {
856 ProblemImpl problem_impl;
857 Solver::Options options;
858 Solver::Summary summary;
859 double x;
860 problem_impl.AddResidualBlock(new FailingCostFunction, NULL, &x);
861 SolverImpl::Solve(options, &problem_impl, &summary);
862 EXPECT_EQ(summary.termination_type, NUMERICAL_FAILURE);
863 EXPECT_EQ(summary.error, "Unable to evaluate the initial cost.");
864}
865
866TEST(SolverImpl, ProblemIsConstant) {
867 ProblemImpl problem_impl;
868 Solver::Options options;
869 Solver::Summary summary;
870 double x = 1;
871 problem_impl.AddResidualBlock(new UnaryIdentityCostFunction, NULL, &x);
872 problem_impl.SetParameterBlockConstant(&x);
873 SolverImpl::Solve(options, &problem_impl, &summary);
874 EXPECT_EQ(summary.termination_type, FUNCTION_TOLERANCE);
875 EXPECT_EQ(summary.initial_cost, 1.0 / 2.0);
876 EXPECT_EQ(summary.final_cost, 1.0 / 2.0);
877}
878
Keir Mierle8ebb0732012-04-30 23:09:08 -0700879} // namespace internal
880} // namespace ceres