blob: ab40e05da35c793b3b768f7c27c73fb69fd74bb8 [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// 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
Keir Mierle04938ef2013-02-17 12:37:55 -0800505TEST_P(DynamicProblem, RemoveParameterBlockWithNoResiduals) {
506 problem->AddParameterBlock(y, 4);
507 problem->AddParameterBlock(z, 5);
508 problem->AddParameterBlock(w, 3);
509 ASSERT_EQ(3, problem->NumParameterBlocks());
510 ASSERT_EQ(0, problem->NumResidualBlocks());
511 EXPECT_EQ(y, GetParameterBlock(0)->user_state());
512 EXPECT_EQ(z, GetParameterBlock(1)->user_state());
513 EXPECT_EQ(w, GetParameterBlock(2)->user_state());
514
515 // w is at the end, which might break the swapping logic so try adding and
516 // removing it.
517 problem->RemoveParameterBlock(w);
518 ASSERT_EQ(2, problem->NumParameterBlocks());
519 ASSERT_EQ(0, problem->NumResidualBlocks());
520 EXPECT_EQ(y, GetParameterBlock(0)->user_state());
521 EXPECT_EQ(z, GetParameterBlock(1)->user_state());
522 problem->AddParameterBlock(w, 3);
523 ASSERT_EQ(3, problem->NumParameterBlocks());
524 ASSERT_EQ(0, problem->NumResidualBlocks());
525 EXPECT_EQ(y, GetParameterBlock(0)->user_state());
526 EXPECT_EQ(z, GetParameterBlock(1)->user_state());
527 EXPECT_EQ(w, GetParameterBlock(2)->user_state());
528
529 // Now remove z, which is in the middle, and add it back.
530 problem->RemoveParameterBlock(z);
531 ASSERT_EQ(2, problem->NumParameterBlocks());
532 ASSERT_EQ(0, problem->NumResidualBlocks());
533 EXPECT_EQ(y, GetParameterBlock(0)->user_state());
534 EXPECT_EQ(w, GetParameterBlock(1)->user_state());
535 problem->AddParameterBlock(z, 5);
536 ASSERT_EQ(3, problem->NumParameterBlocks());
537 ASSERT_EQ(0, problem->NumResidualBlocks());
538 EXPECT_EQ(y, GetParameterBlock(0)->user_state());
539 EXPECT_EQ(w, GetParameterBlock(1)->user_state());
540 EXPECT_EQ(z, GetParameterBlock(2)->user_state());
541
542 // Now remove everything.
543 // y
544 problem->RemoveParameterBlock(y);
545 ASSERT_EQ(2, problem->NumParameterBlocks());
546 ASSERT_EQ(0, problem->NumResidualBlocks());
547 EXPECT_EQ(z, GetParameterBlock(0)->user_state());
548 EXPECT_EQ(w, GetParameterBlock(1)->user_state());
549
550 // z
551 problem->RemoveParameterBlock(z);
552 ASSERT_EQ(1, problem->NumParameterBlocks());
553 ASSERT_EQ(0, problem->NumResidualBlocks());
554 EXPECT_EQ(w, GetParameterBlock(0)->user_state());
555
556 // w
557 problem->RemoveParameterBlock(w);
558 EXPECT_EQ(0, problem->NumParameterBlocks());
559 EXPECT_EQ(0, problem->NumResidualBlocks());
560}
561
562TEST_P(DynamicProblem, RemoveParameterBlockWithResiduals) {
563 problem->AddParameterBlock(y, 4);
564 problem->AddParameterBlock(z, 5);
565 problem->AddParameterBlock(w, 3);
566 ASSERT_EQ(3, problem->NumParameterBlocks());
567 ASSERT_EQ(0, problem->NumResidualBlocks());
568 EXPECT_EQ(y, GetParameterBlock(0)->user_state());
569 EXPECT_EQ(z, GetParameterBlock(1)->user_state());
570 EXPECT_EQ(w, GetParameterBlock(2)->user_state());
571
572 // Add all combinations of cost functions.
573 CostFunction* cost_yzw = new TernaryCostFunction(1, 4, 5, 3);
574 CostFunction* cost_yz = new BinaryCostFunction (1, 4, 5);
575 CostFunction* cost_yw = new BinaryCostFunction (1, 4, 3);
576 CostFunction* cost_zw = new BinaryCostFunction (1, 5, 3);
577 CostFunction* cost_y = new UnaryCostFunction (1, 4);
578 CostFunction* cost_z = new UnaryCostFunction (1, 5);
579 CostFunction* cost_w = new UnaryCostFunction (1, 3);
580
581 ResidualBlock* r_yzw = problem->AddResidualBlock(cost_yzw, NULL, y, z, w);
582 ResidualBlock* r_yz = problem->AddResidualBlock(cost_yz, NULL, y, z);
583 ResidualBlock* r_yw = problem->AddResidualBlock(cost_yw, NULL, y, w);
584 ResidualBlock* r_zw = problem->AddResidualBlock(cost_zw, NULL, z, w);
585 ResidualBlock* r_y = problem->AddResidualBlock(cost_y, NULL, y);
586 ResidualBlock* r_z = problem->AddResidualBlock(cost_z, NULL, z);
587 ResidualBlock* r_w = problem->AddResidualBlock(cost_w, NULL, w);
588
589 EXPECT_EQ(3, problem->NumParameterBlocks());
590 EXPECT_EQ(7, problem->NumResidualBlocks());
591
592 // Remove w, which should remove r_yzw, r_yw, r_zw, r_w.
593 problem->RemoveParameterBlock(w);
594 ASSERT_EQ(2, problem->NumParameterBlocks());
595 ASSERT_EQ(3, problem->NumResidualBlocks());
596
597 ASSERT_FALSE(HasResidualBlock(r_yzw));
598 ASSERT_TRUE (HasResidualBlock(r_yz ));
599 ASSERT_FALSE(HasResidualBlock(r_yw ));
600 ASSERT_FALSE(HasResidualBlock(r_zw ));
601 ASSERT_TRUE (HasResidualBlock(r_y ));
602 ASSERT_TRUE (HasResidualBlock(r_z ));
603 ASSERT_FALSE(HasResidualBlock(r_w ));
604
605 // Remove z, which will remove almost everything else.
606 problem->RemoveParameterBlock(z);
607 ASSERT_EQ(1, problem->NumParameterBlocks());
608 ASSERT_EQ(1, problem->NumResidualBlocks());
609
610 ASSERT_FALSE(HasResidualBlock(r_yzw));
611 ASSERT_FALSE(HasResidualBlock(r_yz ));
612 ASSERT_FALSE(HasResidualBlock(r_yw ));
613 ASSERT_FALSE(HasResidualBlock(r_zw ));
614 ASSERT_TRUE (HasResidualBlock(r_y ));
615 ASSERT_FALSE(HasResidualBlock(r_z ));
616 ASSERT_FALSE(HasResidualBlock(r_w ));
617
618 // Remove y; all gone.
619 problem->RemoveParameterBlock(y);
620 EXPECT_EQ(0, problem->NumParameterBlocks());
621 EXPECT_EQ(0, problem->NumResidualBlocks());
622}
623
624TEST_P(DynamicProblem, RemoveResidualBlock) {
625 problem->AddParameterBlock(y, 4);
626 problem->AddParameterBlock(z, 5);
627 problem->AddParameterBlock(w, 3);
628
629 // Add all combinations of cost functions.
630 CostFunction* cost_yzw = new TernaryCostFunction(1, 4, 5, 3);
631 CostFunction* cost_yz = new BinaryCostFunction (1, 4, 5);
632 CostFunction* cost_yw = new BinaryCostFunction (1, 4, 3);
633 CostFunction* cost_zw = new BinaryCostFunction (1, 5, 3);
634 CostFunction* cost_y = new UnaryCostFunction (1, 4);
635 CostFunction* cost_z = new UnaryCostFunction (1, 5);
636 CostFunction* cost_w = new UnaryCostFunction (1, 3);
637
638 ResidualBlock* r_yzw = problem->AddResidualBlock(cost_yzw, NULL, y, z, w);
639 ResidualBlock* r_yz = problem->AddResidualBlock(cost_yz, NULL, y, z);
640 ResidualBlock* r_yw = problem->AddResidualBlock(cost_yw, NULL, y, w);
641 ResidualBlock* r_zw = problem->AddResidualBlock(cost_zw, NULL, z, w);
642 ResidualBlock* r_y = problem->AddResidualBlock(cost_y, NULL, y);
643 ResidualBlock* r_z = problem->AddResidualBlock(cost_z, NULL, z);
644 ResidualBlock* r_w = problem->AddResidualBlock(cost_w, NULL, w);
645
646 if (GetParam()) {
647 // In this test parameterization, there should be back-pointers from the
648 // parameter blocks to the residual blocks.
649 ExpectParameterBlockContains(y, r_yzw, r_yz, r_yw, r_y);
650 ExpectParameterBlockContains(z, r_yzw, r_yz, r_zw, r_z);
651 ExpectParameterBlockContains(w, r_yzw, r_yw, r_zw, r_w);
652 } else {
653 // Otherwise, nothing.
654 EXPECT_TRUE(GetParameterBlock(0)->mutable_residual_blocks() == NULL);
655 EXPECT_TRUE(GetParameterBlock(1)->mutable_residual_blocks() == NULL);
656 EXPECT_TRUE(GetParameterBlock(2)->mutable_residual_blocks() == NULL);
657 }
658 EXPECT_EQ(3, problem->NumParameterBlocks());
659 EXPECT_EQ(7, problem->NumResidualBlocks());
660
661 // Remove each residual and check the state after each removal.
662
663 // Remove r_yzw.
664 problem->RemoveResidualBlock(r_yzw);
665 ASSERT_EQ(3, problem->NumParameterBlocks());
666 ASSERT_EQ(6, problem->NumResidualBlocks());
667 if (GetParam()) {
668 ExpectParameterBlockContains(y, r_yz, r_yw, r_y);
669 ExpectParameterBlockContains(z, r_yz, r_zw, r_z);
670 ExpectParameterBlockContains(w, r_yw, r_zw, r_w);
671 }
672 ASSERT_TRUE (HasResidualBlock(r_yz ));
673 ASSERT_TRUE (HasResidualBlock(r_yw ));
674 ASSERT_TRUE (HasResidualBlock(r_zw ));
675 ASSERT_TRUE (HasResidualBlock(r_y ));
676 ASSERT_TRUE (HasResidualBlock(r_z ));
677 ASSERT_TRUE (HasResidualBlock(r_w ));
678
679 // Remove r_yw.
680 problem->RemoveResidualBlock(r_yw);
681 ASSERT_EQ(3, problem->NumParameterBlocks());
682 ASSERT_EQ(5, problem->NumResidualBlocks());
683 if (GetParam()) {
684 ExpectParameterBlockContains(y, r_yz, r_y);
685 ExpectParameterBlockContains(z, r_yz, r_zw, r_z);
686 ExpectParameterBlockContains(w, r_zw, r_w);
687 }
688 ASSERT_TRUE (HasResidualBlock(r_yz ));
689 ASSERT_TRUE (HasResidualBlock(r_zw ));
690 ASSERT_TRUE (HasResidualBlock(r_y ));
691 ASSERT_TRUE (HasResidualBlock(r_z ));
692 ASSERT_TRUE (HasResidualBlock(r_w ));
693
694 // Remove r_zw.
695 problem->RemoveResidualBlock(r_zw);
696 ASSERT_EQ(3, problem->NumParameterBlocks());
697 ASSERT_EQ(4, problem->NumResidualBlocks());
698 if (GetParam()) {
699 ExpectParameterBlockContains(y, r_yz, r_y);
700 ExpectParameterBlockContains(z, r_yz, r_z);
701 ExpectParameterBlockContains(w, r_w);
702 }
703 ASSERT_TRUE (HasResidualBlock(r_yz ));
704 ASSERT_TRUE (HasResidualBlock(r_y ));
705 ASSERT_TRUE (HasResidualBlock(r_z ));
706 ASSERT_TRUE (HasResidualBlock(r_w ));
707
708 // Remove r_w.
709 problem->RemoveResidualBlock(r_w);
710 ASSERT_EQ(3, problem->NumParameterBlocks());
711 ASSERT_EQ(3, problem->NumResidualBlocks());
712 if (GetParam()) {
713 ExpectParameterBlockContains(y, r_yz, r_y);
714 ExpectParameterBlockContains(z, r_yz, r_z);
715 ExpectParameterBlockContains(w);
716 }
717 ASSERT_TRUE (HasResidualBlock(r_yz ));
718 ASSERT_TRUE (HasResidualBlock(r_y ));
719 ASSERT_TRUE (HasResidualBlock(r_z ));
720
721 // Remove r_yz.
722 problem->RemoveResidualBlock(r_yz);
723 ASSERT_EQ(3, problem->NumParameterBlocks());
724 ASSERT_EQ(2, problem->NumResidualBlocks());
725 if (GetParam()) {
726 ExpectParameterBlockContains(y, r_y);
727 ExpectParameterBlockContains(z, r_z);
728 ExpectParameterBlockContains(w);
729 }
730 ASSERT_TRUE (HasResidualBlock(r_y ));
731 ASSERT_TRUE (HasResidualBlock(r_z ));
732
733 // Remove the last two.
734 problem->RemoveResidualBlock(r_z);
735 problem->RemoveResidualBlock(r_y);
736 ASSERT_EQ(3, problem->NumParameterBlocks());
737 ASSERT_EQ(0, problem->NumResidualBlocks());
738 if (GetParam()) {
739 ExpectParameterBlockContains(y);
740 ExpectParameterBlockContains(z);
741 ExpectParameterBlockContains(w);
742 }
743}
744
745INSTANTIATE_TEST_CASE_P(OptionsInstantiation,
746 DynamicProblem,
747 ::testing::Values(true, false));
748
Sameer Agarwal509f68c2013-02-20 01:39:03 -0800749// Test for Problem::Evaluate
750
Sameer Agarwal509f68c2013-02-20 01:39:03 -0800751// r_i = i - (j + 1) * x_ij^2
Sameer Agarwal931c3092013-02-25 09:46:21 -0800752template <int kNumResiduals, int kNumParameterBlocks>
Sameer Agarwal509f68c2013-02-20 01:39:03 -0800753class QuadraticCostFunction : public CostFunction {
754 public:
755 QuadraticCostFunction() {
756 CHECK_GT(kNumResiduals, 0);
757 CHECK_GT(kNumParameterBlocks, 0);
758 set_num_residuals(kNumResiduals);
759 for (int i = 0; i < kNumParameterBlocks; ++i) {
760 mutable_parameter_block_sizes()->push_back(kNumResiduals);
761 }
762 }
763
764 virtual bool Evaluate(double const* const* parameters,
765 double* residuals,
766 double** jacobians) const {
767 for (int i = 0; i < kNumResiduals; ++i) {
768 residuals[i] = i;
769 for (int j = 0; j < kNumParameterBlocks; ++j) {
770 residuals[i] -= (j + 1.0) * parameters[j][i] * parameters[j][i];
771 }
772 }
773
774 if (jacobians == NULL) {
775 return true;
776 }
777
778 for (int j = 0; j < kNumParameterBlocks; ++j) {
779 if (jacobians[j] != NULL) {
780 MatrixRef(jacobians[j], kNumResiduals, kNumResiduals) =
781 (-2.0 * (j + 1.0) *
782 ConstVectorRef(parameters[j], kNumResiduals)).asDiagonal();
783 }
784 }
785
786 return true;
787 }
788};
789
790// Convert a CRSMatrix to a dense Eigen matrix.
791void CRSToDenseMatrix(const CRSMatrix& input, Matrix* output) {
792 Matrix& m = *CHECK_NOTNULL(output);
793 m.resize(input.num_rows, input.num_cols);
794 m.setZero();
795 for (int row = 0; row < input.num_rows; ++row) {
796 for (int j = input.rows[row]; j < input.rows[row + 1]; ++j) {
797 const int col = input.cols[j];
798 m(row, col) = input.values[j];
799 }
800 }
801}
802
803class ProblemEvaluateTest : public ::testing::Test {
804 protected:
805 void SetUp() {
806 for (int i = 0; i < 6; ++i) {
807 parameters_[i] = static_cast<double>(i + 1);
808 }
809
810 parameter_blocks_.push_back(parameters_);
811 parameter_blocks_.push_back(parameters_ + 2);
812 parameter_blocks_.push_back(parameters_ + 4);
813
814
815 CostFunction* cost_function = new QuadraticCostFunction<2, 2>;
816
817 // f(x, y)
818 residual_blocks_.push_back(
819 problem_.AddResidualBlock(cost_function,
820 NULL,
821 parameters_,
822 parameters_ + 2));
823 // g(y, z)
824 residual_blocks_.push_back(
825 problem_.AddResidualBlock(cost_function,
826 NULL, parameters_ + 2,
827 parameters_ + 4));
828 // h(z, x)
829 residual_blocks_.push_back(
830 problem_.AddResidualBlock(cost_function,
831 NULL,
832 parameters_ + 4,
833 parameters_));
834 }
835
836
837
838 void EvaluateAndCompare(const Problem::EvaluateOptions& options,
839 const int expected_num_rows,
840 const int expected_num_cols,
841 const double expected_cost,
842 const double* expected_residuals,
843 const double* expected_gradient,
844 const double* expected_jacobian) {
845 double cost;
846 vector<double> residuals;
847 vector<double> gradient;
848 CRSMatrix jacobian;
849
850 EXPECT_TRUE(
851 problem_.Evaluate(options,
852 &cost,
853 expected_residuals != NULL ? &residuals : NULL,
854 expected_gradient != NULL ? &gradient : NULL,
855 expected_jacobian != NULL ? &jacobian : NULL));
856
857 if (expected_residuals != NULL) {
858 EXPECT_EQ(residuals.size(), expected_num_rows);
859 }
860
861 if (expected_gradient != NULL) {
862 EXPECT_EQ(gradient.size(), expected_num_cols);
863 }
864
865 if (expected_jacobian != NULL) {
866 EXPECT_EQ(jacobian.num_rows, expected_num_rows);
867 EXPECT_EQ(jacobian.num_cols, expected_num_cols);
868 }
869
870 Matrix dense_jacobian;
871 if (expected_jacobian != NULL) {
872 CRSToDenseMatrix(jacobian, &dense_jacobian);
873 }
874
875 CompareEvaluations(expected_num_rows,
876 expected_num_cols,
877 expected_cost,
878 expected_residuals,
879 expected_gradient,
880 expected_jacobian,
881 cost,
882 residuals.size() > 0 ? &residuals[0] : NULL,
883 gradient.size() > 0 ? &gradient[0] : NULL,
884 dense_jacobian.data());
885 }
886
887 void CheckAllEvaluationCombinations(const Problem::EvaluateOptions& options,
888 const ExpectedEvaluation& expected) {
889 for (int i = 0; i < 8; ++i) {
890 EvaluateAndCompare(options,
891 expected.num_rows,
892 expected.num_cols,
893 expected.cost,
894 (i & 1) ? expected.residuals : NULL,
895 (i & 2) ? expected.gradient : NULL,
896 (i & 4) ? expected.jacobian : NULL);
897 }
898 }
899
900 ProblemImpl problem_;
901 double parameters_[6];
902 vector<double*> parameter_blocks_;
903 vector<ResidualBlockId> residual_blocks_;
904};
905
906
907TEST_F(ProblemEvaluateTest, MultipleParameterAndResidualBlocks) {
908 ExpectedEvaluation expected = {
909 // Rows/columns
910 6, 6,
911 // Cost
912 7607.0,
913 // Residuals
914 { -19.0, -35.0, // f
915 -59.0, -87.0, // g
916 -27.0, -43.0 // h
917 },
918 // Gradient
919 { 146.0, 484.0, // x
920 582.0, 1256.0, // y
921 1450.0, 2604.0, // z
922 },
923 // Jacobian
924 // x y z
925 { /* f(x, y) */ -2.0, 0.0, -12.0, 0.0, 0.0, 0.0,
926 0.0, -4.0, 0.0, -16.0, 0.0, 0.0,
927 /* g(y, z) */ 0.0, 0.0, -6.0, 0.0, -20.0, 0.0,
928 0.0, 0.0, 0.0, -8.0, 0.0, -24.0,
929 /* h(z, x) */ -4.0, 0.0, 0.0, 0.0, -10.0, 0.0,
930 0.0, -8.0, 0.0, 0.0, 0.0, -12.0
931 }
932 };
933
934 CheckAllEvaluationCombinations(Problem::EvaluateOptions(), expected);
935}
936
937TEST_F(ProblemEvaluateTest, ParameterAndResidualBlocksPassedInOptions) {
938 ExpectedEvaluation expected = {
939 // Rows/columns
940 6, 6,
941 // Cost
942 7607.0,
943 // Residuals
944 { -19.0, -35.0, // f
945 -59.0, -87.0, // g
946 -27.0, -43.0 // h
947 },
948 // Gradient
949 { 146.0, 484.0, // x
950 582.0, 1256.0, // y
951 1450.0, 2604.0, // z
952 },
953 // Jacobian
954 // x y z
955 { /* f(x, y) */ -2.0, 0.0, -12.0, 0.0, 0.0, 0.0,
956 0.0, -4.0, 0.0, -16.0, 0.0, 0.0,
957 /* g(y, z) */ 0.0, 0.0, -6.0, 0.0, -20.0, 0.0,
958 0.0, 0.0, 0.0, -8.0, 0.0, -24.0,
959 /* h(z, x) */ -4.0, 0.0, 0.0, 0.0, -10.0, 0.0,
960 0.0, -8.0, 0.0, 0.0, 0.0, -12.0
961 }
962 };
963
964 Problem::EvaluateOptions evaluate_options;
965 evaluate_options.parameter_blocks = parameter_blocks_;
966 evaluate_options.residual_blocks = residual_blocks_;
967 CheckAllEvaluationCombinations(evaluate_options, expected);
968}
969
970TEST_F(ProblemEvaluateTest, ReorderedResidualBlocks) {
971 ExpectedEvaluation expected = {
972 // Rows/columns
973 6, 6,
974 // Cost
975 7607.0,
976 // Residuals
977 { -19.0, -35.0, // f
978 -27.0, -43.0, // h
979 -59.0, -87.0 // g
980 },
981 // Gradient
982 { 146.0, 484.0, // x
983 582.0, 1256.0, // y
984 1450.0, 2604.0, // z
985 },
986 // Jacobian
987 // x y z
988 { /* f(x, y) */ -2.0, 0.0, -12.0, 0.0, 0.0, 0.0,
989 0.0, -4.0, 0.0, -16.0, 0.0, 0.0,
990 /* h(z, x) */ -4.0, 0.0, 0.0, 0.0, -10.0, 0.0,
991 0.0, -8.0, 0.0, 0.0, 0.0, -12.0,
992 /* g(y, z) */ 0.0, 0.0, -6.0, 0.0, -20.0, 0.0,
993 0.0, 0.0, 0.0, -8.0, 0.0, -24.0
994 }
995 };
996
997 Problem::EvaluateOptions evaluate_options;
998 evaluate_options.parameter_blocks = parameter_blocks_;
999
1000 // f, h, g
1001 evaluate_options.residual_blocks.push_back(residual_blocks_[0]);
1002 evaluate_options.residual_blocks.push_back(residual_blocks_[2]);
1003 evaluate_options.residual_blocks.push_back(residual_blocks_[1]);
1004
1005 CheckAllEvaluationCombinations(evaluate_options, expected);
1006}
1007
1008TEST_F(ProblemEvaluateTest, ReorderedResidualBlocksAndReorderedParameterBlocks) {
1009 ExpectedEvaluation expected = {
1010 // Rows/columns
1011 6, 6,
1012 // Cost
1013 7607.0,
1014 // Residuals
1015 { -19.0, -35.0, // f
1016 -27.0, -43.0, // h
1017 -59.0, -87.0 // g
1018 },
1019 // Gradient
1020 { 1450.0, 2604.0, // z
1021 582.0, 1256.0, // y
1022 146.0, 484.0, // x
1023 },
1024 // Jacobian
1025 // z y x
1026 { /* f(x, y) */ 0.0, 0.0, -12.0, 0.0, -2.0, 0.0,
1027 0.0, 0.0, 0.0, -16.0, 0.0, -4.0,
1028 /* h(z, x) */ -10.0, 0.0, 0.0, 0.0, -4.0, 0.0,
1029 0.0, -12.0, 0.0, 0.0, 0.0, -8.0,
1030 /* g(y, z) */ -20.0, 0.0, -6.0, 0.0, 0.0, 0.0,
1031 0.0, -24.0, 0.0, -8.0, 0.0, 0.0
1032 }
1033 };
1034
1035 Problem::EvaluateOptions evaluate_options;
1036 // z, y, x
1037 evaluate_options.parameter_blocks.push_back(parameter_blocks_[2]);
1038 evaluate_options.parameter_blocks.push_back(parameter_blocks_[1]);
1039 evaluate_options.parameter_blocks.push_back(parameter_blocks_[0]);
1040
1041 // f, h, g
1042 evaluate_options.residual_blocks.push_back(residual_blocks_[0]);
1043 evaluate_options.residual_blocks.push_back(residual_blocks_[2]);
1044 evaluate_options.residual_blocks.push_back(residual_blocks_[1]);
1045
1046 CheckAllEvaluationCombinations(evaluate_options, expected);
1047}
1048
1049TEST_F(ProblemEvaluateTest, ConstantParameterBlock) {
1050 ExpectedEvaluation expected = {
1051 // Rows/columns
1052 6, 6,
1053 // Cost
1054 7607.0,
1055 // Residuals
1056 { -19.0, -35.0, // f
1057 -59.0, -87.0, // g
1058 -27.0, -43.0 // h
1059 },
1060
1061 // Gradient
1062 { 146.0, 484.0, // x
1063 0.0, 0.0, // y
1064 1450.0, 2604.0, // z
1065 },
1066
1067 // Jacobian
1068 // x y z
1069 { /* f(x, y) */ -2.0, 0.0, 0.0, 0.0, 0.0, 0.0,
1070 0.0, -4.0, 0.0, 0.0, 0.0, 0.0,
1071 /* g(y, z) */ 0.0, 0.0, 0.0, 0.0, -20.0, 0.0,
1072 0.0, 0.0, 0.0, 0.0, 0.0, -24.0,
1073 /* h(z, x) */ -4.0, 0.0, 0.0, 0.0, -10.0, 0.0,
1074 0.0, -8.0, 0.0, 0.0, 0.0, -12.0
1075 }
1076 };
1077
1078 problem_.SetParameterBlockConstant(parameters_ + 2);
1079 CheckAllEvaluationCombinations(Problem::EvaluateOptions(), expected);
1080}
1081
1082TEST_F(ProblemEvaluateTest, ExcludedAResidualBlock) {
1083 ExpectedEvaluation expected = {
1084 // Rows/columns
1085 4, 6,
1086 // Cost
1087 2082.0,
1088 // Residuals
1089 { -19.0, -35.0, // f
1090 -27.0, -43.0 // h
1091 },
1092 // Gradient
1093 { 146.0, 484.0, // x
1094 228.0, 560.0, // y
1095 270.0, 516.0, // z
1096 },
1097 // Jacobian
1098 // x y z
1099 { /* f(x, y) */ -2.0, 0.0, -12.0, 0.0, 0.0, 0.0,
1100 0.0, -4.0, 0.0, -16.0, 0.0, 0.0,
1101 /* h(z, x) */ -4.0, 0.0, 0.0, 0.0, -10.0, 0.0,
1102 0.0, -8.0, 0.0, 0.0, 0.0, -12.0
1103 }
1104 };
1105
1106 Problem::EvaluateOptions evaluate_options;
1107 evaluate_options.residual_blocks.push_back(residual_blocks_[0]);
1108 evaluate_options.residual_blocks.push_back(residual_blocks_[2]);
1109
1110 CheckAllEvaluationCombinations(evaluate_options, expected);
1111}
1112
1113TEST_F(ProblemEvaluateTest, ExcludedParameterBlock) {
1114 ExpectedEvaluation expected = {
1115 // Rows/columns
1116 6, 4,
1117 // Cost
1118 7607.0,
1119 // Residuals
1120 { -19.0, -35.0, // f
1121 -59.0, -87.0, // g
1122 -27.0, -43.0 // h
1123 },
1124
1125 // Gradient
1126 { 146.0, 484.0, // x
1127 1450.0, 2604.0, // z
1128 },
1129
1130 // Jacobian
1131 // x z
1132 { /* f(x, y) */ -2.0, 0.0, 0.0, 0.0,
1133 0.0, -4.0, 0.0, 0.0,
1134 /* g(y, z) */ 0.0, 0.0, -20.0, 0.0,
1135 0.0, 0.0, 0.0, -24.0,
1136 /* h(z, x) */ -4.0, 0.0, -10.0, 0.0,
1137 0.0, -8.0, 0.0, -12.0
1138 }
1139 };
1140
1141 Problem::EvaluateOptions evaluate_options;
1142 // x, z
1143 evaluate_options.parameter_blocks.push_back(parameter_blocks_[0]);
1144 evaluate_options.parameter_blocks.push_back(parameter_blocks_[2]);
1145 evaluate_options.residual_blocks = residual_blocks_;
1146 CheckAllEvaluationCombinations(evaluate_options, expected);
1147}
1148
1149TEST_F(ProblemEvaluateTest, ExcludedParameterBlockAndExcludedResidualBlock) {
1150 ExpectedEvaluation expected = {
1151 // Rows/columns
1152 4, 4,
1153 // Cost
1154 6318.0,
1155 // Residuals
1156 { -19.0, -35.0, // f
1157 -59.0, -87.0, // g
1158 },
1159
1160 // Gradient
1161 { 38.0, 140.0, // x
1162 1180.0, 2088.0, // z
1163 },
1164
1165 // Jacobian
1166 // x z
1167 { /* f(x, y) */ -2.0, 0.0, 0.0, 0.0,
1168 0.0, -4.0, 0.0, 0.0,
1169 /* g(y, z) */ 0.0, 0.0, -20.0, 0.0,
1170 0.0, 0.0, 0.0, -24.0,
1171 }
1172 };
1173
1174 Problem::EvaluateOptions evaluate_options;
1175 // x, z
1176 evaluate_options.parameter_blocks.push_back(parameter_blocks_[0]);
1177 evaluate_options.parameter_blocks.push_back(parameter_blocks_[2]);
1178 evaluate_options.residual_blocks.push_back(residual_blocks_[0]);
1179 evaluate_options.residual_blocks.push_back(residual_blocks_[1]);
1180
1181 CheckAllEvaluationCombinations(evaluate_options, expected);
1182}
1183
1184TEST_F(ProblemEvaluateTest, LocalParameterization) {
1185 ExpectedEvaluation expected = {
1186 // Rows/columns
1187 6, 5,
1188 // Cost
1189 7607.0,
1190 // Residuals
1191 { -19.0, -35.0, // f
1192 -59.0, -87.0, // g
1193 -27.0, -43.0 // h
1194 },
1195 // Gradient
1196 { 146.0, 484.0, // x
1197 1256.0, // y with SubsetParameterization
1198 1450.0, 2604.0, // z
1199 },
1200 // Jacobian
1201 // x y z
1202 { /* f(x, y) */ -2.0, 0.0, 0.0, 0.0, 0.0,
1203 0.0, -4.0, -16.0, 0.0, 0.0,
1204 /* g(y, z) */ 0.0, 0.0, 0.0, -20.0, 0.0,
1205 0.0, 0.0, -8.0, 0.0, -24.0,
1206 /* h(z, x) */ -4.0, 0.0, 0.0, -10.0, 0.0,
1207 0.0, -8.0, 0.0, 0.0, -12.0
1208 }
1209 };
1210
1211 vector<int> constant_parameters;
1212 constant_parameters.push_back(0);
1213 problem_.SetParameterization(parameters_ + 2,
1214 new SubsetParameterization(2,
1215 constant_parameters));
1216
1217 CheckAllEvaluationCombinations(Problem::EvaluateOptions(), expected);
1218}
1219
Keir Mierle8ebb0732012-04-30 23:09:08 -07001220} // namespace internal
1221} // namespace ceres