blob: a7f4f0bd92ce83e5e1a5adc0f41f4b44dadd3c0c [file] [log] [blame]
Keir Mierle8ebb0732012-04-30 23:09:08 -07001// Ceres Solver - A fast non-linear least squares minimizer
Keir Mierlefda69b52013-10-10 00:25:24 -07002// Copyright 2013 Google Inc. All rights reserved.
Keir Mierle8ebb0732012-04-30 23:09:08 -07003// 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// keir@google.com (Keir Mierle)
31
32#include "ceres/problem.h"
Keir Mierle04938ef2013-02-17 12:37:55 -080033#include "ceres/problem_impl.h"
Keir Mierle8ebb0732012-04-30 23:09:08 -070034
Sameer Agarwal509f68c2013-02-20 01:39:03 -080035#include "ceres/casts.h"
Keir Mierle8ebb0732012-04-30 23:09:08 -070036#include "ceres/cost_function.h"
Sameer Agarwal509f68c2013-02-20 01:39:03 -080037#include "ceres/crs_matrix.h"
Sameer Agarwal039ff072013-02-26 09:15:39 -080038#include "ceres/evaluator_test_utils.cc"
Sameer Agarwal509f68c2013-02-20 01:39:03 -080039#include "ceres/internal/eigen.h"
40#include "ceres/internal/scoped_ptr.h"
Keir Mierle8ebb0732012-04-30 23:09:08 -070041#include "ceres/local_parameterization.h"
Keir Mierle04938ef2013-02-17 12:37:55 -080042#include "ceres/map_util.h"
43#include "ceres/parameter_block.h"
44#include "ceres/program.h"
Keir Mierle8ebb0732012-04-30 23:09:08 -070045#include "ceres/sized_cost_function.h"
Sameer Agarwal509f68c2013-02-20 01:39:03 -080046#include "ceres/sparse_matrix.h"
47#include "ceres/types.h"
48#include "gtest/gtest.h"
49
Keir Mierle8ebb0732012-04-30 23:09:08 -070050namespace ceres {
51namespace internal {
52
53// The following three classes are for the purposes of defining
54// function signatures. They have dummy Evaluate functions.
55
56// Trivial cost function that accepts a single argument.
57class UnaryCostFunction : public CostFunction {
58 public:
59 UnaryCostFunction(int num_residuals, int16 parameter_block_size) {
60 set_num_residuals(num_residuals);
61 mutable_parameter_block_sizes()->push_back(parameter_block_size);
62 }
63 virtual ~UnaryCostFunction() {}
64
65 virtual bool Evaluate(double const* const* parameters,
66 double* residuals,
67 double** jacobians) const {
68 for (int i = 0; i < num_residuals(); ++i) {
69 residuals[i] = 1;
70 }
71 return true;
72 }
73};
74
75// Trivial cost function that accepts two arguments.
76class BinaryCostFunction: public CostFunction {
77 public:
78 BinaryCostFunction(int num_residuals,
79 int16 parameter_block1_size,
80 int16 parameter_block2_size) {
81 set_num_residuals(num_residuals);
82 mutable_parameter_block_sizes()->push_back(parameter_block1_size);
83 mutable_parameter_block_sizes()->push_back(parameter_block2_size);
84 }
85
86 virtual bool Evaluate(double const* const* parameters,
87 double* residuals,
88 double** jacobians) const {
89 for (int i = 0; i < num_residuals(); ++i) {
90 residuals[i] = 2;
91 }
92 return true;
93 }
94};
95
96// Trivial cost function that accepts three arguments.
97class TernaryCostFunction: public CostFunction {
98 public:
99 TernaryCostFunction(int num_residuals,
100 int16 parameter_block1_size,
101 int16 parameter_block2_size,
102 int16 parameter_block3_size) {
103 set_num_residuals(num_residuals);
104 mutable_parameter_block_sizes()->push_back(parameter_block1_size);
105 mutable_parameter_block_sizes()->push_back(parameter_block2_size);
106 mutable_parameter_block_sizes()->push_back(parameter_block3_size);
107 }
108
109 virtual bool Evaluate(double const* const* parameters,
110 double* residuals,
111 double** jacobians) const {
112 for (int i = 0; i < num_residuals(); ++i) {
113 residuals[i] = 3;
114 }
115 return true;
116 }
117};
118
119TEST(Problem, AddResidualWithNullCostFunctionDies) {
120 double x[3], y[4], z[5];
121
122 Problem problem;
123 problem.AddParameterBlock(x, 3);
124 problem.AddParameterBlock(y, 4);
125 problem.AddParameterBlock(z, 5);
126
Sameer Agarwalc0149972012-09-18 13:55:18 -0700127 EXPECT_DEATH_IF_SUPPORTED(problem.AddResidualBlock(NULL, NULL, x),
128 "'cost_function' Must be non NULL");
Keir Mierle8ebb0732012-04-30 23:09:08 -0700129}
130
131TEST(Problem, AddResidualWithIncorrectNumberOfParameterBlocksDies) {
132 double x[3], y[4], z[5];
133
134 Problem problem;
135 problem.AddParameterBlock(x, 3);
136 problem.AddParameterBlock(y, 4);
137 problem.AddParameterBlock(z, 5);
138
139 // UnaryCostFunction takes only one parameter, but two are passed.
Sameer Agarwalc0149972012-09-18 13:55:18 -0700140 EXPECT_DEATH_IF_SUPPORTED(
Keir Mierle8ebb0732012-04-30 23:09:08 -0700141 problem.AddResidualBlock(new UnaryCostFunction(2, 3), NULL, x, y),
142 "parameter_blocks.size()");
143}
144
145TEST(Problem, AddResidualWithDifferentSizesOnTheSameVariableDies) {
146 double x[3];
147
148 Problem problem;
149 problem.AddResidualBlock(new UnaryCostFunction(2, 3), NULL, x);
Sameer Agarwalc0149972012-09-18 13:55:18 -0700150 EXPECT_DEATH_IF_SUPPORTED(problem.AddResidualBlock(
151 new UnaryCostFunction(
152 2, 4 /* 4 != 3 */), NULL, x),
153 "different block sizes");
Keir Mierle8ebb0732012-04-30 23:09:08 -0700154}
155
156TEST(Problem, AddResidualWithDuplicateParametersDies) {
157 double x[3], z[5];
158
159 Problem problem;
Sameer Agarwalc0149972012-09-18 13:55:18 -0700160 EXPECT_DEATH_IF_SUPPORTED(problem.AddResidualBlock(
161 new BinaryCostFunction(2, 3, 3), NULL, x, x),
162 "Duplicate parameter blocks");
163 EXPECT_DEATH_IF_SUPPORTED(problem.AddResidualBlock(
164 new TernaryCostFunction(1, 5, 3, 5),
165 NULL, z, x, z),
166 "Duplicate parameter blocks");
Keir Mierle8ebb0732012-04-30 23:09:08 -0700167}
168
169TEST(Problem, AddResidualWithIncorrectSizesOfParameterBlockDies) {
170 double x[3], y[4], z[5];
171
172 Problem problem;
173 problem.AddParameterBlock(x, 3);
174 problem.AddParameterBlock(y, 4);
175 problem.AddParameterBlock(z, 5);
176
177 // The cost function expects the size of the second parameter, z, to be 4
178 // instead of 5 as declared above. This is fatal.
Sameer Agarwalc0149972012-09-18 13:55:18 -0700179 EXPECT_DEATH_IF_SUPPORTED(problem.AddResidualBlock(
Keir Mierle8ebb0732012-04-30 23:09:08 -0700180 new BinaryCostFunction(2, 3, 4), NULL, x, z),
181 "different block sizes");
182}
183
184TEST(Problem, AddResidualAddsDuplicatedParametersOnlyOnce) {
185 double x[3], y[4], z[5];
186
187 Problem problem;
188 problem.AddResidualBlock(new UnaryCostFunction(2, 3), NULL, x);
189 problem.AddResidualBlock(new UnaryCostFunction(2, 3), NULL, x);
190 problem.AddResidualBlock(new UnaryCostFunction(2, 4), NULL, y);
191 problem.AddResidualBlock(new UnaryCostFunction(2, 5), NULL, z);
192
193 EXPECT_EQ(3, problem.NumParameterBlocks());
194 EXPECT_EQ(12, problem.NumParameters());
195}
196
197TEST(Problem, AddParameterWithDifferentSizesOnTheSameVariableDies) {
198 double x[3], y[4];
199
200 Problem problem;
201 problem.AddParameterBlock(x, 3);
202 problem.AddParameterBlock(y, 4);
203
Sameer Agarwalc0149972012-09-18 13:55:18 -0700204 EXPECT_DEATH_IF_SUPPORTED(problem.AddParameterBlock(x, 4),
205 "different block sizes");
Keir Mierle8ebb0732012-04-30 23:09:08 -0700206}
207
208static double *IntToPtr(int i) {
209 return reinterpret_cast<double*>(sizeof(double) * i); // NOLINT
210}
211
212TEST(Problem, AddParameterWithAliasedParametersDies) {
213 // Layout is
214 //
215 // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
216 // [x] x x x x [y] y y
217 // o==o==o o==o==o o==o
218 // o--o--o o--o--o o--o o--o--o
219 //
220 // Parameter block additions are tested as listed above; expected successful
221 // ones marked with o==o and aliasing ones marked with o--o.
222
223 Problem problem;
224 problem.AddParameterBlock(IntToPtr(5), 5); // x
225 problem.AddParameterBlock(IntToPtr(13), 3); // y
226
Sameer Agarwalc0149972012-09-18 13:55:18 -0700227 EXPECT_DEATH_IF_SUPPORTED(problem.AddParameterBlock(IntToPtr( 4), 2),
228 "Aliasing detected");
229 EXPECT_DEATH_IF_SUPPORTED(problem.AddParameterBlock(IntToPtr( 4), 3),
230 "Aliasing detected");
231 EXPECT_DEATH_IF_SUPPORTED(problem.AddParameterBlock(IntToPtr( 4), 9),
232 "Aliasing detected");
233 EXPECT_DEATH_IF_SUPPORTED(problem.AddParameterBlock(IntToPtr( 8), 3),
234 "Aliasing detected");
235 EXPECT_DEATH_IF_SUPPORTED(problem.AddParameterBlock(IntToPtr(12), 2),
236 "Aliasing detected");
237 EXPECT_DEATH_IF_SUPPORTED(problem.AddParameterBlock(IntToPtr(14), 3),
238 "Aliasing detected");
Keir Mierle8ebb0732012-04-30 23:09:08 -0700239
240 // These ones should work.
241 problem.AddParameterBlock(IntToPtr( 2), 3);
242 problem.AddParameterBlock(IntToPtr(10), 3);
243 problem.AddParameterBlock(IntToPtr(16), 2);
244
245 ASSERT_EQ(5, problem.NumParameterBlocks());
246}
247
Keir Mierle8ebb0732012-04-30 23:09:08 -0700248TEST(Problem, AddParameterIgnoresDuplicateCalls) {
249 double x[3], y[4];
250
251 Problem problem;
252 problem.AddParameterBlock(x, 3);
253 problem.AddParameterBlock(y, 4);
254
255 // Creating parameter blocks multiple times is ignored.
256 problem.AddParameterBlock(x, 3);
257 problem.AddResidualBlock(new UnaryCostFunction(2, 3), NULL, x);
258
259 // ... even repeatedly.
260 problem.AddParameterBlock(x, 3);
261 problem.AddResidualBlock(new UnaryCostFunction(2, 3), NULL, x);
262
263 // More parameters are fine.
264 problem.AddParameterBlock(y, 4);
265 problem.AddResidualBlock(new UnaryCostFunction(2, 4), NULL, y);
266
267 EXPECT_EQ(2, problem.NumParameterBlocks());
268 EXPECT_EQ(7, problem.NumParameters());
269}
270
271TEST(Problem, AddingParametersAndResidualsResultsInExpectedProblem) {
272 double x[3], y[4], z[5], w[4];
273
274 Problem problem;
275 problem.AddParameterBlock(x, 3);
276 EXPECT_EQ(1, problem.NumParameterBlocks());
277 EXPECT_EQ(3, problem.NumParameters());
278
279 problem.AddParameterBlock(y, 4);
280 EXPECT_EQ(2, problem.NumParameterBlocks());
281 EXPECT_EQ(7, problem.NumParameters());
282
283 problem.AddParameterBlock(z, 5);
284 EXPECT_EQ(3, problem.NumParameterBlocks());
285 EXPECT_EQ(12, problem.NumParameters());
286
287 // Add a parameter that has a local parameterization.
Sameer Agarwal9123e2f2012-09-18 21:49:06 -0700288 w[0] = 1.0; w[1] = 0.0; w[2] = 0.0; w[3] = 0.0;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700289 problem.AddParameterBlock(w, 4, new QuaternionParameterization);
290 EXPECT_EQ(4, problem.NumParameterBlocks());
291 EXPECT_EQ(16, problem.NumParameters());
292
293 problem.AddResidualBlock(new UnaryCostFunction(2, 3), NULL, x);
294 problem.AddResidualBlock(new BinaryCostFunction(6, 5, 4) , NULL, z, y);
295 problem.AddResidualBlock(new BinaryCostFunction(3, 3, 5), NULL, x, z);
296 problem.AddResidualBlock(new BinaryCostFunction(7, 5, 3), NULL, z, x);
297 problem.AddResidualBlock(new TernaryCostFunction(1, 5, 3, 4), NULL, z, x, y);
298
299 const int total_residuals = 2 + 6 + 3 + 7 + 1;
300 EXPECT_EQ(problem.NumResidualBlocks(), 5);
301 EXPECT_EQ(problem.NumResiduals(), total_residuals);
302}
303
304class DestructorCountingCostFunction : public SizedCostFunction<3, 4, 5> {
305 public:
Keir Mierle04938ef2013-02-17 12:37:55 -0800306 explicit DestructorCountingCostFunction(int *num_destructions)
307 : num_destructions_(num_destructions) {}
Keir Mierle8ebb0732012-04-30 23:09:08 -0700308
309 virtual ~DestructorCountingCostFunction() {
Keir Mierle04938ef2013-02-17 12:37:55 -0800310 *num_destructions_ += 1;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700311 }
312
313 virtual bool Evaluate(double const* const* parameters,
314 double* residuals,
315 double** jacobians) const {
316 return true;
317 }
318
319 private:
Keir Mierle04938ef2013-02-17 12:37:55 -0800320 int* num_destructions_;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700321};
322
323TEST(Problem, ReusedCostFunctionsAreOnlyDeletedOnce) {
324 double y[4], z[5];
Keir Mierle04938ef2013-02-17 12:37:55 -0800325 int num_destructions = 0;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700326
327 // Add a cost function multiple times and check to make sure that
328 // the destructor on the cost function is only called once.
329 {
330 Problem problem;
331 problem.AddParameterBlock(y, 4);
332 problem.AddParameterBlock(z, 5);
333
Keir Mierle04938ef2013-02-17 12:37:55 -0800334 CostFunction* cost = new DestructorCountingCostFunction(&num_destructions);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700335 problem.AddResidualBlock(cost, NULL, y, z);
336 problem.AddResidualBlock(cost, NULL, y, z);
337 problem.AddResidualBlock(cost, NULL, y, z);
Keir Mierle04938ef2013-02-17 12:37:55 -0800338 EXPECT_EQ(3, problem.NumResidualBlocks());
Keir Mierle8ebb0732012-04-30 23:09:08 -0700339 }
340
341 // Check that the destructor was called only once.
Keir Mierle04938ef2013-02-17 12:37:55 -0800342 CHECK_EQ(num_destructions, 1);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700343}
344
Keir Mierle04938ef2013-02-17 12:37:55 -0800345TEST(Problem, CostFunctionsAreDeletedEvenWithRemovals) {
346 double y[4], z[5], w[4];
347 int num_destructions = 0;
348 {
349 Problem problem;
350 problem.AddParameterBlock(y, 4);
351 problem.AddParameterBlock(z, 5);
352
353 CostFunction* cost_yz =
354 new DestructorCountingCostFunction(&num_destructions);
355 CostFunction* cost_wz =
356 new DestructorCountingCostFunction(&num_destructions);
357 ResidualBlock* r_yz = problem.AddResidualBlock(cost_yz, NULL, y, z);
358 ResidualBlock* r_wz = problem.AddResidualBlock(cost_wz, NULL, w, z);
359 EXPECT_EQ(2, problem.NumResidualBlocks());
360
361 // In the current implementation, the destructor shouldn't get run yet.
362 problem.RemoveResidualBlock(r_yz);
363 CHECK_EQ(num_destructions, 0);
364 problem.RemoveResidualBlock(r_wz);
365 CHECK_EQ(num_destructions, 0);
366
367 EXPECT_EQ(0, problem.NumResidualBlocks());
368 }
369 CHECK_EQ(num_destructions, 2);
370}
371
372// Make the dynamic problem tests (e.g. for removing residual blocks)
373// parameterized on whether the low-latency mode is enabled or not.
374//
375// This tests against ProblemImpl instead of Problem in order to inspect the
376// state of the resulting Program; this is difficult with only the thin Problem
377// interface.
378struct DynamicProblem : public ::testing::TestWithParam<bool> {
379 DynamicProblem() {
380 Problem::Options options;
381 options.enable_fast_parameter_block_removal = GetParam();
382 problem.reset(new ProblemImpl(options));
383 }
384
385 ParameterBlock* GetParameterBlock(int block) {
386 return problem->program().parameter_blocks()[block];
387 }
388 ResidualBlock* GetResidualBlock(int block) {
389 return problem->program().residual_blocks()[block];
390 }
391
392 bool HasResidualBlock(ResidualBlock* residual_block) {
393 return find(problem->program().residual_blocks().begin(),
394 problem->program().residual_blocks().end(),
395 residual_block) != problem->program().residual_blocks().end();
396 }
397
398 // The next block of functions until the end are only for testing the
399 // residual block removals.
400 void ExpectParameterBlockContainsResidualBlock(
401 double* values,
402 ResidualBlock* residual_block) {
403 ParameterBlock* parameter_block =
404 FindOrDie(problem->parameter_map(), values);
405 EXPECT_TRUE(ContainsKey(*(parameter_block->mutable_residual_blocks()),
406 residual_block));
407 }
408
409 void ExpectSize(double* values, int size) {
410 ParameterBlock* parameter_block =
411 FindOrDie(problem->parameter_map(), values);
412 EXPECT_EQ(size, parameter_block->mutable_residual_blocks()->size());
413 }
414
415 // Degenerate case.
416 void ExpectParameterBlockContains(double* values) {
417 ExpectSize(values, 0);
418 }
419
420 void ExpectParameterBlockContains(double* values,
421 ResidualBlock* r1) {
422 ExpectSize(values, 1);
423 ExpectParameterBlockContainsResidualBlock(values, r1);
424 }
425
426 void ExpectParameterBlockContains(double* values,
427 ResidualBlock* r1,
428 ResidualBlock* r2) {
429 ExpectSize(values, 2);
430 ExpectParameterBlockContainsResidualBlock(values, r1);
431 ExpectParameterBlockContainsResidualBlock(values, r2);
432 }
433
434 void ExpectParameterBlockContains(double* values,
435 ResidualBlock* r1,
436 ResidualBlock* r2,
437 ResidualBlock* r3) {
438 ExpectSize(values, 3);
439 ExpectParameterBlockContainsResidualBlock(values, r1);
440 ExpectParameterBlockContainsResidualBlock(values, r2);
441 ExpectParameterBlockContainsResidualBlock(values, r3);
442 }
443
444 void ExpectParameterBlockContains(double* values,
445 ResidualBlock* r1,
446 ResidualBlock* r2,
447 ResidualBlock* r3,
448 ResidualBlock* r4) {
449 ExpectSize(values, 4);
450 ExpectParameterBlockContainsResidualBlock(values, r1);
451 ExpectParameterBlockContainsResidualBlock(values, r2);
452 ExpectParameterBlockContainsResidualBlock(values, r3);
453 ExpectParameterBlockContainsResidualBlock(values, r4);
454 }
455
456 scoped_ptr<ProblemImpl> problem;
457 double y[4], z[5], w[3];
458};
459
Sameer Agarwal020d8e12013-03-06 16:19:26 -0800460TEST(Problem, SetParameterBlockConstantWithUnknownPtrDies) {
461 double x[3];
462 double y[2];
463
464 Problem problem;
465 problem.AddParameterBlock(x, 3);
466
467 EXPECT_DEATH_IF_SUPPORTED(problem.SetParameterBlockConstant(y),
468 "Parameter block not found:");
469}
470
471TEST(Problem, SetParameterBlockVariableWithUnknownPtrDies) {
472 double x[3];
473 double y[2];
474
475 Problem problem;
476 problem.AddParameterBlock(x, 3);
477
478 EXPECT_DEATH_IF_SUPPORTED(problem.SetParameterBlockVariable(y),
479 "Parameter block not found:");
480}
481
482TEST(Problem, SetLocalParameterizationWithUnknownPtrDies) {
483 double x[3];
484 double y[2];
485
486 Problem problem;
487 problem.AddParameterBlock(x, 3);
488
489 EXPECT_DEATH_IF_SUPPORTED(
490 problem.SetParameterization(y, new IdentityParameterization(3)),
491 "Parameter block not found:");
492}
493
494TEST(Problem, RemoveParameterBlockWithUnknownPtrDies) {
495 double x[3];
496 double y[2];
497
498 Problem problem;
499 problem.AddParameterBlock(x, 3);
500
501 EXPECT_DEATH_IF_SUPPORTED(
502 problem.RemoveParameterBlock(y), "Parameter block not found:");
503}
504
Sameer Agarwal3d954692013-04-18 14:54:55 -0700505TEST(Problem, ParameterBlockQueryTest) {
506 double x[3];
507 double y[4];
508 Problem problem;
509 problem.AddParameterBlock(x, 3);
510 problem.AddParameterBlock(y, 4);
511
512 vector<int> constant_parameters;
513 constant_parameters.push_back(0);
514 problem.SetParameterization(
515 x,
516 new SubsetParameterization(3, constant_parameters));
517 EXPECT_EQ(problem.ParameterBlockSize(x), 3);
518 EXPECT_EQ(problem.ParameterBlockLocalSize(x), 2);
519 EXPECT_EQ(problem.ParameterBlockLocalSize(y), 4);
520
521 vector<double*> parameter_blocks;
522 problem.GetParameterBlocks(&parameter_blocks);
523 EXPECT_EQ(parameter_blocks.size(), 2);
524 EXPECT_NE(parameter_blocks[0], parameter_blocks[1]);
525 EXPECT_TRUE(parameter_blocks[0] == x || parameter_blocks[0] == y);
526 EXPECT_TRUE(parameter_blocks[1] == x || parameter_blocks[1] == y);
527
528 problem.RemoveParameterBlock(x);
529 problem.GetParameterBlocks(&parameter_blocks);
530 EXPECT_EQ(parameter_blocks.size(), 1);
531 EXPECT_TRUE(parameter_blocks[0] == y);
532}
533
Keir Mierle04938ef2013-02-17 12:37:55 -0800534TEST_P(DynamicProblem, RemoveParameterBlockWithNoResiduals) {
535 problem->AddParameterBlock(y, 4);
536 problem->AddParameterBlock(z, 5);
537 problem->AddParameterBlock(w, 3);
538 ASSERT_EQ(3, problem->NumParameterBlocks());
539 ASSERT_EQ(0, problem->NumResidualBlocks());
540 EXPECT_EQ(y, GetParameterBlock(0)->user_state());
541 EXPECT_EQ(z, GetParameterBlock(1)->user_state());
542 EXPECT_EQ(w, GetParameterBlock(2)->user_state());
543
544 // w is at the end, which might break the swapping logic so try adding and
545 // removing it.
546 problem->RemoveParameterBlock(w);
547 ASSERT_EQ(2, problem->NumParameterBlocks());
548 ASSERT_EQ(0, problem->NumResidualBlocks());
549 EXPECT_EQ(y, GetParameterBlock(0)->user_state());
550 EXPECT_EQ(z, GetParameterBlock(1)->user_state());
551 problem->AddParameterBlock(w, 3);
552 ASSERT_EQ(3, problem->NumParameterBlocks());
553 ASSERT_EQ(0, problem->NumResidualBlocks());
554 EXPECT_EQ(y, GetParameterBlock(0)->user_state());
555 EXPECT_EQ(z, GetParameterBlock(1)->user_state());
556 EXPECT_EQ(w, GetParameterBlock(2)->user_state());
557
558 // Now remove z, which is in the middle, and add it back.
559 problem->RemoveParameterBlock(z);
560 ASSERT_EQ(2, problem->NumParameterBlocks());
561 ASSERT_EQ(0, problem->NumResidualBlocks());
562 EXPECT_EQ(y, GetParameterBlock(0)->user_state());
563 EXPECT_EQ(w, GetParameterBlock(1)->user_state());
564 problem->AddParameterBlock(z, 5);
565 ASSERT_EQ(3, problem->NumParameterBlocks());
566 ASSERT_EQ(0, problem->NumResidualBlocks());
567 EXPECT_EQ(y, GetParameterBlock(0)->user_state());
568 EXPECT_EQ(w, GetParameterBlock(1)->user_state());
569 EXPECT_EQ(z, GetParameterBlock(2)->user_state());
570
571 // Now remove everything.
572 // y
573 problem->RemoveParameterBlock(y);
574 ASSERT_EQ(2, problem->NumParameterBlocks());
575 ASSERT_EQ(0, problem->NumResidualBlocks());
576 EXPECT_EQ(z, GetParameterBlock(0)->user_state());
577 EXPECT_EQ(w, GetParameterBlock(1)->user_state());
578
579 // z
580 problem->RemoveParameterBlock(z);
581 ASSERT_EQ(1, problem->NumParameterBlocks());
582 ASSERT_EQ(0, problem->NumResidualBlocks());
583 EXPECT_EQ(w, GetParameterBlock(0)->user_state());
584
585 // w
586 problem->RemoveParameterBlock(w);
587 EXPECT_EQ(0, problem->NumParameterBlocks());
588 EXPECT_EQ(0, problem->NumResidualBlocks());
589}
590
591TEST_P(DynamicProblem, RemoveParameterBlockWithResiduals) {
592 problem->AddParameterBlock(y, 4);
593 problem->AddParameterBlock(z, 5);
594 problem->AddParameterBlock(w, 3);
595 ASSERT_EQ(3, problem->NumParameterBlocks());
596 ASSERT_EQ(0, problem->NumResidualBlocks());
597 EXPECT_EQ(y, GetParameterBlock(0)->user_state());
598 EXPECT_EQ(z, GetParameterBlock(1)->user_state());
599 EXPECT_EQ(w, GetParameterBlock(2)->user_state());
600
601 // Add all combinations of cost functions.
602 CostFunction* cost_yzw = new TernaryCostFunction(1, 4, 5, 3);
603 CostFunction* cost_yz = new BinaryCostFunction (1, 4, 5);
604 CostFunction* cost_yw = new BinaryCostFunction (1, 4, 3);
605 CostFunction* cost_zw = new BinaryCostFunction (1, 5, 3);
606 CostFunction* cost_y = new UnaryCostFunction (1, 4);
607 CostFunction* cost_z = new UnaryCostFunction (1, 5);
608 CostFunction* cost_w = new UnaryCostFunction (1, 3);
609
610 ResidualBlock* r_yzw = problem->AddResidualBlock(cost_yzw, NULL, y, z, w);
611 ResidualBlock* r_yz = problem->AddResidualBlock(cost_yz, NULL, y, z);
612 ResidualBlock* r_yw = problem->AddResidualBlock(cost_yw, NULL, y, w);
613 ResidualBlock* r_zw = problem->AddResidualBlock(cost_zw, NULL, z, w);
614 ResidualBlock* r_y = problem->AddResidualBlock(cost_y, NULL, y);
615 ResidualBlock* r_z = problem->AddResidualBlock(cost_z, NULL, z);
616 ResidualBlock* r_w = problem->AddResidualBlock(cost_w, NULL, w);
617
618 EXPECT_EQ(3, problem->NumParameterBlocks());
619 EXPECT_EQ(7, problem->NumResidualBlocks());
620
621 // Remove w, which should remove r_yzw, r_yw, r_zw, r_w.
622 problem->RemoveParameterBlock(w);
623 ASSERT_EQ(2, problem->NumParameterBlocks());
624 ASSERT_EQ(3, problem->NumResidualBlocks());
625
626 ASSERT_FALSE(HasResidualBlock(r_yzw));
627 ASSERT_TRUE (HasResidualBlock(r_yz ));
628 ASSERT_FALSE(HasResidualBlock(r_yw ));
629 ASSERT_FALSE(HasResidualBlock(r_zw ));
630 ASSERT_TRUE (HasResidualBlock(r_y ));
631 ASSERT_TRUE (HasResidualBlock(r_z ));
632 ASSERT_FALSE(HasResidualBlock(r_w ));
633
634 // Remove z, which will remove almost everything else.
635 problem->RemoveParameterBlock(z);
636 ASSERT_EQ(1, problem->NumParameterBlocks());
637 ASSERT_EQ(1, problem->NumResidualBlocks());
638
639 ASSERT_FALSE(HasResidualBlock(r_yzw));
640 ASSERT_FALSE(HasResidualBlock(r_yz ));
641 ASSERT_FALSE(HasResidualBlock(r_yw ));
642 ASSERT_FALSE(HasResidualBlock(r_zw ));
643 ASSERT_TRUE (HasResidualBlock(r_y ));
644 ASSERT_FALSE(HasResidualBlock(r_z ));
645 ASSERT_FALSE(HasResidualBlock(r_w ));
646
647 // Remove y; all gone.
648 problem->RemoveParameterBlock(y);
649 EXPECT_EQ(0, problem->NumParameterBlocks());
650 EXPECT_EQ(0, problem->NumResidualBlocks());
651}
652
653TEST_P(DynamicProblem, RemoveResidualBlock) {
654 problem->AddParameterBlock(y, 4);
655 problem->AddParameterBlock(z, 5);
656 problem->AddParameterBlock(w, 3);
657
658 // Add all combinations of cost functions.
659 CostFunction* cost_yzw = new TernaryCostFunction(1, 4, 5, 3);
660 CostFunction* cost_yz = new BinaryCostFunction (1, 4, 5);
661 CostFunction* cost_yw = new BinaryCostFunction (1, 4, 3);
662 CostFunction* cost_zw = new BinaryCostFunction (1, 5, 3);
663 CostFunction* cost_y = new UnaryCostFunction (1, 4);
664 CostFunction* cost_z = new UnaryCostFunction (1, 5);
665 CostFunction* cost_w = new UnaryCostFunction (1, 3);
666
667 ResidualBlock* r_yzw = problem->AddResidualBlock(cost_yzw, NULL, y, z, w);
668 ResidualBlock* r_yz = problem->AddResidualBlock(cost_yz, NULL, y, z);
669 ResidualBlock* r_yw = problem->AddResidualBlock(cost_yw, NULL, y, w);
670 ResidualBlock* r_zw = problem->AddResidualBlock(cost_zw, NULL, z, w);
671 ResidualBlock* r_y = problem->AddResidualBlock(cost_y, NULL, y);
672 ResidualBlock* r_z = problem->AddResidualBlock(cost_z, NULL, z);
673 ResidualBlock* r_w = problem->AddResidualBlock(cost_w, NULL, w);
674
675 if (GetParam()) {
676 // In this test parameterization, there should be back-pointers from the
677 // parameter blocks to the residual blocks.
678 ExpectParameterBlockContains(y, r_yzw, r_yz, r_yw, r_y);
679 ExpectParameterBlockContains(z, r_yzw, r_yz, r_zw, r_z);
680 ExpectParameterBlockContains(w, r_yzw, r_yw, r_zw, r_w);
681 } else {
682 // Otherwise, nothing.
683 EXPECT_TRUE(GetParameterBlock(0)->mutable_residual_blocks() == NULL);
684 EXPECT_TRUE(GetParameterBlock(1)->mutable_residual_blocks() == NULL);
685 EXPECT_TRUE(GetParameterBlock(2)->mutable_residual_blocks() == NULL);
686 }
687 EXPECT_EQ(3, problem->NumParameterBlocks());
688 EXPECT_EQ(7, problem->NumResidualBlocks());
689
690 // Remove each residual and check the state after each removal.
691
692 // Remove r_yzw.
693 problem->RemoveResidualBlock(r_yzw);
694 ASSERT_EQ(3, problem->NumParameterBlocks());
695 ASSERT_EQ(6, problem->NumResidualBlocks());
696 if (GetParam()) {
697 ExpectParameterBlockContains(y, r_yz, r_yw, r_y);
698 ExpectParameterBlockContains(z, r_yz, r_zw, r_z);
699 ExpectParameterBlockContains(w, r_yw, r_zw, r_w);
700 }
701 ASSERT_TRUE (HasResidualBlock(r_yz ));
702 ASSERT_TRUE (HasResidualBlock(r_yw ));
703 ASSERT_TRUE (HasResidualBlock(r_zw ));
704 ASSERT_TRUE (HasResidualBlock(r_y ));
705 ASSERT_TRUE (HasResidualBlock(r_z ));
706 ASSERT_TRUE (HasResidualBlock(r_w ));
707
708 // Remove r_yw.
709 problem->RemoveResidualBlock(r_yw);
710 ASSERT_EQ(3, problem->NumParameterBlocks());
711 ASSERT_EQ(5, problem->NumResidualBlocks());
712 if (GetParam()) {
713 ExpectParameterBlockContains(y, r_yz, r_y);
714 ExpectParameterBlockContains(z, r_yz, r_zw, r_z);
715 ExpectParameterBlockContains(w, r_zw, r_w);
716 }
717 ASSERT_TRUE (HasResidualBlock(r_yz ));
718 ASSERT_TRUE (HasResidualBlock(r_zw ));
719 ASSERT_TRUE (HasResidualBlock(r_y ));
720 ASSERT_TRUE (HasResidualBlock(r_z ));
721 ASSERT_TRUE (HasResidualBlock(r_w ));
722
723 // Remove r_zw.
724 problem->RemoveResidualBlock(r_zw);
725 ASSERT_EQ(3, problem->NumParameterBlocks());
726 ASSERT_EQ(4, problem->NumResidualBlocks());
727 if (GetParam()) {
728 ExpectParameterBlockContains(y, r_yz, r_y);
729 ExpectParameterBlockContains(z, r_yz, r_z);
730 ExpectParameterBlockContains(w, r_w);
731 }
732 ASSERT_TRUE (HasResidualBlock(r_yz ));
733 ASSERT_TRUE (HasResidualBlock(r_y ));
734 ASSERT_TRUE (HasResidualBlock(r_z ));
735 ASSERT_TRUE (HasResidualBlock(r_w ));
736
737 // Remove r_w.
738 problem->RemoveResidualBlock(r_w);
739 ASSERT_EQ(3, problem->NumParameterBlocks());
740 ASSERT_EQ(3, problem->NumResidualBlocks());
741 if (GetParam()) {
742 ExpectParameterBlockContains(y, r_yz, r_y);
743 ExpectParameterBlockContains(z, r_yz, r_z);
744 ExpectParameterBlockContains(w);
745 }
746 ASSERT_TRUE (HasResidualBlock(r_yz ));
747 ASSERT_TRUE (HasResidualBlock(r_y ));
748 ASSERT_TRUE (HasResidualBlock(r_z ));
749
750 // Remove r_yz.
751 problem->RemoveResidualBlock(r_yz);
752 ASSERT_EQ(3, problem->NumParameterBlocks());
753 ASSERT_EQ(2, problem->NumResidualBlocks());
754 if (GetParam()) {
755 ExpectParameterBlockContains(y, r_y);
756 ExpectParameterBlockContains(z, r_z);
757 ExpectParameterBlockContains(w);
758 }
759 ASSERT_TRUE (HasResidualBlock(r_y ));
760 ASSERT_TRUE (HasResidualBlock(r_z ));
761
762 // Remove the last two.
763 problem->RemoveResidualBlock(r_z);
764 problem->RemoveResidualBlock(r_y);
765 ASSERT_EQ(3, problem->NumParameterBlocks());
766 ASSERT_EQ(0, problem->NumResidualBlocks());
767 if (GetParam()) {
768 ExpectParameterBlockContains(y);
769 ExpectParameterBlockContains(z);
770 ExpectParameterBlockContains(w);
771 }
772}
773
Keir Mierlefda69b52013-10-10 00:25:24 -0700774// Check that a null-terminated array, a, has the same elements as b.
775template<typename T>
776void ExpectVectorContainsUnordered(const T* a, const vector<T>& b) {
777 // Compute the size of a.
778 int size = 0;
779 while (a[size]) {
780 ++size;
781 }
782 ASSERT_EQ(size, b.size());
783
784 // Sort a.
785 vector<T> a_sorted(size);
786 copy(a, a + size, a_sorted.begin());
787 sort(a_sorted.begin(), a_sorted.end());
788
789 // Sort b.
790 vector<T> b_sorted(b);
791 sort(b_sorted.begin(), b_sorted.end());
792
793 // Compare.
794 for (int i = 0; i < size; ++i) {
795 EXPECT_EQ(a_sorted[i], b_sorted[i]);
796 }
797}
798
799void ExpectProblemHasResidualBlocks(
800 const ProblemImpl &problem,
801 const ResidualBlockId *expected_residual_blocks) {
802 vector<ResidualBlockId> residual_blocks;
803 problem.GetResidualBlocks(&residual_blocks);
804 ExpectVectorContainsUnordered(expected_residual_blocks, residual_blocks);
805}
806
807TEST_P(DynamicProblem, GetXXXBlocksForYYYBlock) {
808 problem->AddParameterBlock(y, 4);
809 problem->AddParameterBlock(z, 5);
810 problem->AddParameterBlock(w, 3);
811
812 // Add all combinations of cost functions.
813 CostFunction* cost_yzw = new TernaryCostFunction(1, 4, 5, 3);
814 CostFunction* cost_yz = new BinaryCostFunction (1, 4, 5);
815 CostFunction* cost_yw = new BinaryCostFunction (1, 4, 3);
816 CostFunction* cost_zw = new BinaryCostFunction (1, 5, 3);
817 CostFunction* cost_y = new UnaryCostFunction (1, 4);
818 CostFunction* cost_z = new UnaryCostFunction (1, 5);
819 CostFunction* cost_w = new UnaryCostFunction (1, 3);
820
821 ResidualBlock* r_yzw = problem->AddResidualBlock(cost_yzw, NULL, y, z, w);
822 {
823 ResidualBlockId expected_residuals[] = {r_yzw, 0};
824 ExpectProblemHasResidualBlocks(*problem, expected_residuals);
825 }
826 ResidualBlock* r_yz = problem->AddResidualBlock(cost_yz, NULL, y, z);
827 {
828 ResidualBlockId expected_residuals[] = {r_yzw, r_yz, 0};
829 ExpectProblemHasResidualBlocks(*problem, expected_residuals);
830 }
831 ResidualBlock* r_yw = problem->AddResidualBlock(cost_yw, NULL, y, w);
832 {
833 ResidualBlock *expected_residuals[] = {r_yzw, r_yz, r_yw, 0};
834 ExpectProblemHasResidualBlocks(*problem, expected_residuals);
835 }
836 ResidualBlock* r_zw = problem->AddResidualBlock(cost_zw, NULL, z, w);
837 {
838 ResidualBlock *expected_residuals[] = {r_yzw, r_yz, r_yw, r_zw, 0};
839 ExpectProblemHasResidualBlocks(*problem, expected_residuals);
840 }
841 ResidualBlock* r_y = problem->AddResidualBlock(cost_y, NULL, y);
842 {
843 ResidualBlock *expected_residuals[] = {r_yzw, r_yz, r_yw, r_zw, r_y, 0};
844 ExpectProblemHasResidualBlocks(*problem, expected_residuals);
845 }
846 ResidualBlock* r_z = problem->AddResidualBlock(cost_z, NULL, z);
847 {
848 ResidualBlock *expected_residuals[] = {
849 r_yzw, r_yz, r_yw, r_zw, r_y, r_z, 0
850 };
851 ExpectProblemHasResidualBlocks(*problem, expected_residuals);
852 }
853 ResidualBlock* r_w = problem->AddResidualBlock(cost_w, NULL, w);
854 {
855 ResidualBlock *expected_residuals[] = {
856 r_yzw, r_yz, r_yw, r_zw, r_y, r_z, r_w, 0
857 };
858 ExpectProblemHasResidualBlocks(*problem, expected_residuals);
859 }
860
861 vector<double*> parameter_blocks;
862 vector<ResidualBlockId> residual_blocks;
863
864 // Check GetResidualBlocksForParameterBlock() for all parameter blocks.
865 struct GetResidualBlocksForParameterBlockTestCase {
866 double* parameter_block;
867 ResidualBlockId expected_residual_blocks[10];
868 };
869 GetResidualBlocksForParameterBlockTestCase get_residual_blocks_cases[] = {
870 { y, { r_yzw, r_yz, r_yw, r_y, NULL} },
871 { z, { r_yzw, r_yz, r_zw, r_z, NULL} },
872 { w, { r_yzw, r_yw, r_zw, r_w, NULL} },
873 { NULL }
874 };
875 for (int i = 0; get_residual_blocks_cases[i].parameter_block; ++i) {
876 problem->GetResidualBlocksForParameterBlock(
877 get_residual_blocks_cases[i].parameter_block,
878 &residual_blocks);
879 ExpectVectorContainsUnordered(
880 get_residual_blocks_cases[i].expected_residual_blocks,
881 residual_blocks);
882 }
883
884 // Check GetParameterBlocksForResidualBlock() for all residual blocks.
885 struct GetParameterBlocksForResidualBlockTestCase {
886 ResidualBlockId residual_block;
887 double* expected_parameter_blocks[10];
888 };
889 GetParameterBlocksForResidualBlockTestCase get_parameter_blocks_cases[] = {
890 { r_yzw, { y, z, w, NULL } },
891 { r_yz , { y, z, NULL } },
892 { r_yw , { y, w, NULL } },
893 { r_zw , { z, w, NULL } },
894 { r_y , { y, NULL } },
895 { r_z , { z, NULL } },
896 { r_w , { w, NULL } },
897 { NULL }
898 };
899 for (int i = 0; get_parameter_blocks_cases[i].residual_block; ++i) {
900 problem->GetParameterBlocksForResidualBlock(
901 get_parameter_blocks_cases[i].residual_block,
902 &parameter_blocks);
903 ExpectVectorContainsUnordered(
904 get_parameter_blocks_cases[i].expected_parameter_blocks,
905 parameter_blocks);
906 }
907}
908
Keir Mierle04938ef2013-02-17 12:37:55 -0800909INSTANTIATE_TEST_CASE_P(OptionsInstantiation,
910 DynamicProblem,
911 ::testing::Values(true, false));
912
Sameer Agarwal509f68c2013-02-20 01:39:03 -0800913// Test for Problem::Evaluate
914
Sameer Agarwal509f68c2013-02-20 01:39:03 -0800915// r_i = i - (j + 1) * x_ij^2
Sameer Agarwal931c3092013-02-25 09:46:21 -0800916template <int kNumResiduals, int kNumParameterBlocks>
Sameer Agarwal509f68c2013-02-20 01:39:03 -0800917class QuadraticCostFunction : public CostFunction {
918 public:
919 QuadraticCostFunction() {
920 CHECK_GT(kNumResiduals, 0);
921 CHECK_GT(kNumParameterBlocks, 0);
922 set_num_residuals(kNumResiduals);
923 for (int i = 0; i < kNumParameterBlocks; ++i) {
924 mutable_parameter_block_sizes()->push_back(kNumResiduals);
925 }
926 }
927
928 virtual bool Evaluate(double const* const* parameters,
929 double* residuals,
930 double** jacobians) const {
931 for (int i = 0; i < kNumResiduals; ++i) {
932 residuals[i] = i;
933 for (int j = 0; j < kNumParameterBlocks; ++j) {
934 residuals[i] -= (j + 1.0) * parameters[j][i] * parameters[j][i];
935 }
936 }
937
938 if (jacobians == NULL) {
939 return true;
940 }
941
942 for (int j = 0; j < kNumParameterBlocks; ++j) {
943 if (jacobians[j] != NULL) {
944 MatrixRef(jacobians[j], kNumResiduals, kNumResiduals) =
945 (-2.0 * (j + 1.0) *
946 ConstVectorRef(parameters[j], kNumResiduals)).asDiagonal();
947 }
948 }
949
950 return true;
951 }
952};
953
954// Convert a CRSMatrix to a dense Eigen matrix.
955void CRSToDenseMatrix(const CRSMatrix& input, Matrix* output) {
956 Matrix& m = *CHECK_NOTNULL(output);
957 m.resize(input.num_rows, input.num_cols);
958 m.setZero();
959 for (int row = 0; row < input.num_rows; ++row) {
960 for (int j = input.rows[row]; j < input.rows[row + 1]; ++j) {
961 const int col = input.cols[j];
962 m(row, col) = input.values[j];
963 }
964 }
965}
966
967class ProblemEvaluateTest : public ::testing::Test {
968 protected:
969 void SetUp() {
970 for (int i = 0; i < 6; ++i) {
971 parameters_[i] = static_cast<double>(i + 1);
972 }
973
974 parameter_blocks_.push_back(parameters_);
975 parameter_blocks_.push_back(parameters_ + 2);
976 parameter_blocks_.push_back(parameters_ + 4);
977
978
979 CostFunction* cost_function = new QuadraticCostFunction<2, 2>;
980
981 // f(x, y)
982 residual_blocks_.push_back(
983 problem_.AddResidualBlock(cost_function,
984 NULL,
985 parameters_,
986 parameters_ + 2));
987 // g(y, z)
988 residual_blocks_.push_back(
989 problem_.AddResidualBlock(cost_function,
990 NULL, parameters_ + 2,
991 parameters_ + 4));
992 // h(z, x)
993 residual_blocks_.push_back(
994 problem_.AddResidualBlock(cost_function,
995 NULL,
996 parameters_ + 4,
997 parameters_));
998 }
999
1000
1001
1002 void EvaluateAndCompare(const Problem::EvaluateOptions& options,
1003 const int expected_num_rows,
1004 const int expected_num_cols,
1005 const double expected_cost,
1006 const double* expected_residuals,
1007 const double* expected_gradient,
1008 const double* expected_jacobian) {
1009 double cost;
1010 vector<double> residuals;
1011 vector<double> gradient;
1012 CRSMatrix jacobian;
1013
1014 EXPECT_TRUE(
1015 problem_.Evaluate(options,
1016 &cost,
1017 expected_residuals != NULL ? &residuals : NULL,
1018 expected_gradient != NULL ? &gradient : NULL,
1019 expected_jacobian != NULL ? &jacobian : NULL));
1020
1021 if (expected_residuals != NULL) {
1022 EXPECT_EQ(residuals.size(), expected_num_rows);
1023 }
1024
1025 if (expected_gradient != NULL) {
1026 EXPECT_EQ(gradient.size(), expected_num_cols);
1027 }
1028
1029 if (expected_jacobian != NULL) {
1030 EXPECT_EQ(jacobian.num_rows, expected_num_rows);
1031 EXPECT_EQ(jacobian.num_cols, expected_num_cols);
1032 }
1033
1034 Matrix dense_jacobian;
1035 if (expected_jacobian != NULL) {
1036 CRSToDenseMatrix(jacobian, &dense_jacobian);
1037 }
1038
1039 CompareEvaluations(expected_num_rows,
1040 expected_num_cols,
1041 expected_cost,
1042 expected_residuals,
1043 expected_gradient,
1044 expected_jacobian,
1045 cost,
1046 residuals.size() > 0 ? &residuals[0] : NULL,
1047 gradient.size() > 0 ? &gradient[0] : NULL,
1048 dense_jacobian.data());
1049 }
1050
1051 void CheckAllEvaluationCombinations(const Problem::EvaluateOptions& options,
1052 const ExpectedEvaluation& expected) {
1053 for (int i = 0; i < 8; ++i) {
1054 EvaluateAndCompare(options,
1055 expected.num_rows,
1056 expected.num_cols,
1057 expected.cost,
1058 (i & 1) ? expected.residuals : NULL,
1059 (i & 2) ? expected.gradient : NULL,
1060 (i & 4) ? expected.jacobian : NULL);
1061 }
1062 }
1063
1064 ProblemImpl problem_;
1065 double parameters_[6];
1066 vector<double*> parameter_blocks_;
1067 vector<ResidualBlockId> residual_blocks_;
1068};
1069
1070
1071TEST_F(ProblemEvaluateTest, MultipleParameterAndResidualBlocks) {
1072 ExpectedEvaluation expected = {
1073 // Rows/columns
1074 6, 6,
1075 // Cost
1076 7607.0,
1077 // Residuals
1078 { -19.0, -35.0, // f
1079 -59.0, -87.0, // g
1080 -27.0, -43.0 // h
1081 },
1082 // Gradient
1083 { 146.0, 484.0, // x
1084 582.0, 1256.0, // y
1085 1450.0, 2604.0, // z
1086 },
1087 // Jacobian
1088 // x y z
1089 { /* f(x, y) */ -2.0, 0.0, -12.0, 0.0, 0.0, 0.0,
1090 0.0, -4.0, 0.0, -16.0, 0.0, 0.0,
1091 /* g(y, z) */ 0.0, 0.0, -6.0, 0.0, -20.0, 0.0,
1092 0.0, 0.0, 0.0, -8.0, 0.0, -24.0,
1093 /* h(z, x) */ -4.0, 0.0, 0.0, 0.0, -10.0, 0.0,
1094 0.0, -8.0, 0.0, 0.0, 0.0, -12.0
1095 }
1096 };
1097
1098 CheckAllEvaluationCombinations(Problem::EvaluateOptions(), expected);
1099}
1100
1101TEST_F(ProblemEvaluateTest, ParameterAndResidualBlocksPassedInOptions) {
1102 ExpectedEvaluation expected = {
1103 // Rows/columns
1104 6, 6,
1105 // Cost
1106 7607.0,
1107 // Residuals
1108 { -19.0, -35.0, // f
1109 -59.0, -87.0, // g
1110 -27.0, -43.0 // h
1111 },
1112 // Gradient
1113 { 146.0, 484.0, // x
1114 582.0, 1256.0, // y
1115 1450.0, 2604.0, // z
1116 },
1117 // Jacobian
1118 // x y z
1119 { /* f(x, y) */ -2.0, 0.0, -12.0, 0.0, 0.0, 0.0,
1120 0.0, -4.0, 0.0, -16.0, 0.0, 0.0,
1121 /* g(y, z) */ 0.0, 0.0, -6.0, 0.0, -20.0, 0.0,
1122 0.0, 0.0, 0.0, -8.0, 0.0, -24.0,
1123 /* h(z, x) */ -4.0, 0.0, 0.0, 0.0, -10.0, 0.0,
1124 0.0, -8.0, 0.0, 0.0, 0.0, -12.0
1125 }
1126 };
1127
1128 Problem::EvaluateOptions evaluate_options;
1129 evaluate_options.parameter_blocks = parameter_blocks_;
1130 evaluate_options.residual_blocks = residual_blocks_;
1131 CheckAllEvaluationCombinations(evaluate_options, expected);
1132}
1133
1134TEST_F(ProblemEvaluateTest, ReorderedResidualBlocks) {
1135 ExpectedEvaluation expected = {
1136 // Rows/columns
1137 6, 6,
1138 // Cost
1139 7607.0,
1140 // Residuals
1141 { -19.0, -35.0, // f
1142 -27.0, -43.0, // h
1143 -59.0, -87.0 // g
1144 },
1145 // Gradient
1146 { 146.0, 484.0, // x
1147 582.0, 1256.0, // y
1148 1450.0, 2604.0, // z
1149 },
1150 // Jacobian
1151 // x y z
1152 { /* f(x, y) */ -2.0, 0.0, -12.0, 0.0, 0.0, 0.0,
1153 0.0, -4.0, 0.0, -16.0, 0.0, 0.0,
1154 /* h(z, x) */ -4.0, 0.0, 0.0, 0.0, -10.0, 0.0,
1155 0.0, -8.0, 0.0, 0.0, 0.0, -12.0,
1156 /* g(y, z) */ 0.0, 0.0, -6.0, 0.0, -20.0, 0.0,
1157 0.0, 0.0, 0.0, -8.0, 0.0, -24.0
1158 }
1159 };
1160
1161 Problem::EvaluateOptions evaluate_options;
1162 evaluate_options.parameter_blocks = parameter_blocks_;
1163
1164 // f, h, g
1165 evaluate_options.residual_blocks.push_back(residual_blocks_[0]);
1166 evaluate_options.residual_blocks.push_back(residual_blocks_[2]);
1167 evaluate_options.residual_blocks.push_back(residual_blocks_[1]);
1168
1169 CheckAllEvaluationCombinations(evaluate_options, expected);
1170}
1171
1172TEST_F(ProblemEvaluateTest, ReorderedResidualBlocksAndReorderedParameterBlocks) {
1173 ExpectedEvaluation expected = {
1174 // Rows/columns
1175 6, 6,
1176 // Cost
1177 7607.0,
1178 // Residuals
1179 { -19.0, -35.0, // f
1180 -27.0, -43.0, // h
1181 -59.0, -87.0 // g
1182 },
1183 // Gradient
1184 { 1450.0, 2604.0, // z
1185 582.0, 1256.0, // y
1186 146.0, 484.0, // x
1187 },
1188 // Jacobian
1189 // z y x
1190 { /* f(x, y) */ 0.0, 0.0, -12.0, 0.0, -2.0, 0.0,
1191 0.0, 0.0, 0.0, -16.0, 0.0, -4.0,
1192 /* h(z, x) */ -10.0, 0.0, 0.0, 0.0, -4.0, 0.0,
1193 0.0, -12.0, 0.0, 0.0, 0.0, -8.0,
1194 /* g(y, z) */ -20.0, 0.0, -6.0, 0.0, 0.0, 0.0,
1195 0.0, -24.0, 0.0, -8.0, 0.0, 0.0
1196 }
1197 };
1198
1199 Problem::EvaluateOptions evaluate_options;
1200 // z, y, x
1201 evaluate_options.parameter_blocks.push_back(parameter_blocks_[2]);
1202 evaluate_options.parameter_blocks.push_back(parameter_blocks_[1]);
1203 evaluate_options.parameter_blocks.push_back(parameter_blocks_[0]);
1204
1205 // f, h, g
1206 evaluate_options.residual_blocks.push_back(residual_blocks_[0]);
1207 evaluate_options.residual_blocks.push_back(residual_blocks_[2]);
1208 evaluate_options.residual_blocks.push_back(residual_blocks_[1]);
1209
1210 CheckAllEvaluationCombinations(evaluate_options, expected);
1211}
1212
1213TEST_F(ProblemEvaluateTest, ConstantParameterBlock) {
1214 ExpectedEvaluation expected = {
1215 // Rows/columns
1216 6, 6,
1217 // Cost
1218 7607.0,
1219 // Residuals
1220 { -19.0, -35.0, // f
1221 -59.0, -87.0, // g
1222 -27.0, -43.0 // h
1223 },
1224
1225 // Gradient
1226 { 146.0, 484.0, // x
1227 0.0, 0.0, // y
1228 1450.0, 2604.0, // z
1229 },
1230
1231 // Jacobian
1232 // x y z
1233 { /* f(x, y) */ -2.0, 0.0, 0.0, 0.0, 0.0, 0.0,
1234 0.0, -4.0, 0.0, 0.0, 0.0, 0.0,
1235 /* g(y, z) */ 0.0, 0.0, 0.0, 0.0, -20.0, 0.0,
1236 0.0, 0.0, 0.0, 0.0, 0.0, -24.0,
1237 /* h(z, x) */ -4.0, 0.0, 0.0, 0.0, -10.0, 0.0,
1238 0.0, -8.0, 0.0, 0.0, 0.0, -12.0
1239 }
1240 };
1241
1242 problem_.SetParameterBlockConstant(parameters_ + 2);
1243 CheckAllEvaluationCombinations(Problem::EvaluateOptions(), expected);
1244}
1245
1246TEST_F(ProblemEvaluateTest, ExcludedAResidualBlock) {
1247 ExpectedEvaluation expected = {
1248 // Rows/columns
1249 4, 6,
1250 // Cost
1251 2082.0,
1252 // Residuals
1253 { -19.0, -35.0, // f
1254 -27.0, -43.0 // h
1255 },
1256 // Gradient
1257 { 146.0, 484.0, // x
1258 228.0, 560.0, // y
1259 270.0, 516.0, // z
1260 },
1261 // Jacobian
1262 // x y z
1263 { /* f(x, y) */ -2.0, 0.0, -12.0, 0.0, 0.0, 0.0,
1264 0.0, -4.0, 0.0, -16.0, 0.0, 0.0,
1265 /* h(z, x) */ -4.0, 0.0, 0.0, 0.0, -10.0, 0.0,
1266 0.0, -8.0, 0.0, 0.0, 0.0, -12.0
1267 }
1268 };
1269
1270 Problem::EvaluateOptions evaluate_options;
1271 evaluate_options.residual_blocks.push_back(residual_blocks_[0]);
1272 evaluate_options.residual_blocks.push_back(residual_blocks_[2]);
1273
1274 CheckAllEvaluationCombinations(evaluate_options, expected);
1275}
1276
1277TEST_F(ProblemEvaluateTest, ExcludedParameterBlock) {
1278 ExpectedEvaluation expected = {
1279 // Rows/columns
1280 6, 4,
1281 // Cost
1282 7607.0,
1283 // Residuals
1284 { -19.0, -35.0, // f
1285 -59.0, -87.0, // g
1286 -27.0, -43.0 // h
1287 },
1288
1289 // Gradient
1290 { 146.0, 484.0, // x
1291 1450.0, 2604.0, // z
1292 },
1293
1294 // Jacobian
1295 // x z
1296 { /* f(x, y) */ -2.0, 0.0, 0.0, 0.0,
1297 0.0, -4.0, 0.0, 0.0,
1298 /* g(y, z) */ 0.0, 0.0, -20.0, 0.0,
1299 0.0, 0.0, 0.0, -24.0,
1300 /* h(z, x) */ -4.0, 0.0, -10.0, 0.0,
1301 0.0, -8.0, 0.0, -12.0
1302 }
1303 };
1304
1305 Problem::EvaluateOptions evaluate_options;
1306 // x, z
1307 evaluate_options.parameter_blocks.push_back(parameter_blocks_[0]);
1308 evaluate_options.parameter_blocks.push_back(parameter_blocks_[2]);
1309 evaluate_options.residual_blocks = residual_blocks_;
1310 CheckAllEvaluationCombinations(evaluate_options, expected);
1311}
1312
1313TEST_F(ProblemEvaluateTest, ExcludedParameterBlockAndExcludedResidualBlock) {
1314 ExpectedEvaluation expected = {
1315 // Rows/columns
1316 4, 4,
1317 // Cost
1318 6318.0,
1319 // Residuals
1320 { -19.0, -35.0, // f
1321 -59.0, -87.0, // g
1322 },
1323
1324 // Gradient
1325 { 38.0, 140.0, // x
1326 1180.0, 2088.0, // z
1327 },
1328
1329 // Jacobian
1330 // x z
1331 { /* f(x, y) */ -2.0, 0.0, 0.0, 0.0,
1332 0.0, -4.0, 0.0, 0.0,
1333 /* g(y, z) */ 0.0, 0.0, -20.0, 0.0,
1334 0.0, 0.0, 0.0, -24.0,
1335 }
1336 };
1337
1338 Problem::EvaluateOptions evaluate_options;
1339 // x, z
1340 evaluate_options.parameter_blocks.push_back(parameter_blocks_[0]);
1341 evaluate_options.parameter_blocks.push_back(parameter_blocks_[2]);
1342 evaluate_options.residual_blocks.push_back(residual_blocks_[0]);
1343 evaluate_options.residual_blocks.push_back(residual_blocks_[1]);
1344
1345 CheckAllEvaluationCombinations(evaluate_options, expected);
1346}
1347
1348TEST_F(ProblemEvaluateTest, LocalParameterization) {
1349 ExpectedEvaluation expected = {
1350 // Rows/columns
1351 6, 5,
1352 // Cost
1353 7607.0,
1354 // Residuals
1355 { -19.0, -35.0, // f
1356 -59.0, -87.0, // g
1357 -27.0, -43.0 // h
1358 },
1359 // Gradient
1360 { 146.0, 484.0, // x
1361 1256.0, // y with SubsetParameterization
1362 1450.0, 2604.0, // z
1363 },
1364 // Jacobian
1365 // x y z
1366 { /* f(x, y) */ -2.0, 0.0, 0.0, 0.0, 0.0,
1367 0.0, -4.0, -16.0, 0.0, 0.0,
1368 /* g(y, z) */ 0.0, 0.0, 0.0, -20.0, 0.0,
1369 0.0, 0.0, -8.0, 0.0, -24.0,
1370 /* h(z, x) */ -4.0, 0.0, 0.0, -10.0, 0.0,
1371 0.0, -8.0, 0.0, 0.0, -12.0
1372 }
1373 };
1374
1375 vector<int> constant_parameters;
1376 constant_parameters.push_back(0);
1377 problem_.SetParameterization(parameters_ + 2,
1378 new SubsetParameterization(2,
1379 constant_parameters));
1380
1381 CheckAllEvaluationCombinations(Problem::EvaluateOptions(), expected);
1382}
1383
Keir Mierle8ebb0732012-04-30 23:09:08 -07001384} // namespace internal
1385} // namespace ceres