• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Ceres Solver - A fast non-linear least squares minimizer
2 // Copyright 2013 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"
33 #include "ceres/problem_impl.h"
34 
35 #include "ceres/casts.h"
36 #include "ceres/cost_function.h"
37 #include "ceres/crs_matrix.h"
38 #include "ceres/evaluator_test_utils.cc"
39 #include "ceres/internal/eigen.h"
40 #include "ceres/internal/scoped_ptr.h"
41 #include "ceres/local_parameterization.h"
42 #include "ceres/map_util.h"
43 #include "ceres/parameter_block.h"
44 #include "ceres/program.h"
45 #include "ceres/sized_cost_function.h"
46 #include "ceres/sparse_matrix.h"
47 #include "ceres/types.h"
48 #include "gtest/gtest.h"
49 
50 namespace ceres {
51 namespace 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.
57 class UnaryCostFunction : public CostFunction {
58  public:
UnaryCostFunction(int num_residuals,int32 parameter_block_size)59   UnaryCostFunction(int num_residuals, int32 parameter_block_size) {
60     set_num_residuals(num_residuals);
61     mutable_parameter_block_sizes()->push_back(parameter_block_size);
62   }
~UnaryCostFunction()63   virtual ~UnaryCostFunction() {}
64 
Evaluate(double const * const * parameters,double * residuals,double ** jacobians) const65   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.
76 class BinaryCostFunction: public CostFunction {
77  public:
BinaryCostFunction(int num_residuals,int32 parameter_block1_size,int32 parameter_block2_size)78   BinaryCostFunction(int num_residuals,
79                      int32 parameter_block1_size,
80                      int32 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 
Evaluate(double const * const * parameters,double * residuals,double ** jacobians) const86   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.
97 class TernaryCostFunction: public CostFunction {
98  public:
TernaryCostFunction(int num_residuals,int32 parameter_block1_size,int32 parameter_block2_size,int32 parameter_block3_size)99   TernaryCostFunction(int num_residuals,
100                       int32 parameter_block1_size,
101                       int32 parameter_block2_size,
102                       int32 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 
Evaluate(double const * const * parameters,double * residuals,double ** jacobians) const109   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 
TEST(Problem,AddResidualWithNullCostFunctionDies)119 TEST(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 
127   EXPECT_DEATH_IF_SUPPORTED(problem.AddResidualBlock(NULL, NULL, x),
128                             "'cost_function' Must be non NULL");
129 }
130 
TEST(Problem,AddResidualWithIncorrectNumberOfParameterBlocksDies)131 TEST(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.
140   EXPECT_DEATH_IF_SUPPORTED(
141       problem.AddResidualBlock(new UnaryCostFunction(2, 3), NULL, x, y),
142       "parameter_blocks.size");
143 }
144 
TEST(Problem,AddResidualWithDifferentSizesOnTheSameVariableDies)145 TEST(Problem, AddResidualWithDifferentSizesOnTheSameVariableDies) {
146   double x[3];
147 
148   Problem problem;
149   problem.AddResidualBlock(new UnaryCostFunction(2, 3), NULL, x);
150   EXPECT_DEATH_IF_SUPPORTED(problem.AddResidualBlock(
151                                 new UnaryCostFunction(
152                                     2, 4 /* 4 != 3 */), NULL, x),
153                             "different block sizes");
154 }
155 
TEST(Problem,AddResidualWithDuplicateParametersDies)156 TEST(Problem, AddResidualWithDuplicateParametersDies) {
157   double x[3], z[5];
158 
159   Problem problem;
160   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");
167 }
168 
TEST(Problem,AddResidualWithIncorrectSizesOfParameterBlockDies)169 TEST(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.
179   EXPECT_DEATH_IF_SUPPORTED(problem.AddResidualBlock(
180       new BinaryCostFunction(2, 3, 4), NULL, x, z),
181                "different block sizes");
182 }
183 
TEST(Problem,AddResidualAddsDuplicatedParametersOnlyOnce)184 TEST(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 
TEST(Problem,AddParameterWithDifferentSizesOnTheSameVariableDies)197 TEST(Problem, AddParameterWithDifferentSizesOnTheSameVariableDies) {
198   double x[3], y[4];
199 
200   Problem problem;
201   problem.AddParameterBlock(x, 3);
202   problem.AddParameterBlock(y, 4);
203 
204   EXPECT_DEATH_IF_SUPPORTED(problem.AddParameterBlock(x, 4),
205                             "different block sizes");
206 }
207 
IntToPtr(int i)208 static double *IntToPtr(int i) {
209   return reinterpret_cast<double*>(sizeof(double) * i);  // NOLINT
210 }
211 
TEST(Problem,AddParameterWithAliasedParametersDies)212 TEST(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 
227   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");
239 
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 
TEST(Problem,AddParameterIgnoresDuplicateCalls)248 TEST(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 
TEST(Problem,AddingParametersAndResidualsResultsInExpectedProblem)271 TEST(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.
288   w[0] = 1.0; w[1] = 0.0; w[2] = 0.0; w[3] = 0.0;
289   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 
304 class DestructorCountingCostFunction : public SizedCostFunction<3, 4, 5> {
305  public:
DestructorCountingCostFunction(int * num_destructions)306   explicit DestructorCountingCostFunction(int *num_destructions)
307       : num_destructions_(num_destructions) {}
308 
~DestructorCountingCostFunction()309   virtual ~DestructorCountingCostFunction() {
310     *num_destructions_ += 1;
311   }
312 
Evaluate(double const * const * parameters,double * residuals,double ** jacobians) const313   virtual bool Evaluate(double const* const* parameters,
314                         double* residuals,
315                         double** jacobians) const {
316     return true;
317   }
318 
319  private:
320   int* num_destructions_;
321 };
322 
TEST(Problem,ReusedCostFunctionsAreOnlyDeletedOnce)323 TEST(Problem, ReusedCostFunctionsAreOnlyDeletedOnce) {
324   double y[4], z[5];
325   int num_destructions = 0;
326 
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 
334     CostFunction* cost = new DestructorCountingCostFunction(&num_destructions);
335     problem.AddResidualBlock(cost, NULL, y, z);
336     problem.AddResidualBlock(cost, NULL, y, z);
337     problem.AddResidualBlock(cost, NULL, y, z);
338     EXPECT_EQ(3, problem.NumResidualBlocks());
339   }
340 
341   // Check that the destructor was called only once.
342   CHECK_EQ(num_destructions, 1);
343 }
344 
TEST(Problem,CostFunctionsAreDeletedEvenWithRemovals)345 TEST(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.
378 struct DynamicProblem : public ::testing::TestWithParam<bool> {
DynamicProblemceres::internal::DynamicProblem379   DynamicProblem() {
380     Problem::Options options;
381     options.enable_fast_removal = GetParam();
382     problem.reset(new ProblemImpl(options));
383   }
384 
GetParameterBlockceres::internal::DynamicProblem385   ParameterBlock* GetParameterBlock(int block) {
386     return problem->program().parameter_blocks()[block];
387   }
GetResidualBlockceres::internal::DynamicProblem388   ResidualBlock* GetResidualBlock(int block) {
389     return problem->program().residual_blocks()[block];
390   }
391 
HasResidualBlockceres::internal::DynamicProblem392   bool HasResidualBlock(ResidualBlock* residual_block) {
393     bool have_residual_block = true;
394     if (GetParam()) {
395       have_residual_block &=
396           (problem->residual_block_set().find(residual_block) !=
397            problem->residual_block_set().end());
398     }
399     have_residual_block &=
400         find(problem->program().residual_blocks().begin(),
401              problem->program().residual_blocks().end(),
402              residual_block) != problem->program().residual_blocks().end();
403     return have_residual_block;
404   }
405 
NumResidualBlocksceres::internal::DynamicProblem406   int NumResidualBlocks() {
407     // Verify that the hash set of residuals is maintained consistently.
408     if (GetParam()) {
409       EXPECT_EQ(problem->residual_block_set().size(),
410                 problem->NumResidualBlocks());
411     }
412     return problem->NumResidualBlocks();
413   }
414 
415   // The next block of functions until the end are only for testing the
416   // residual block removals.
ExpectParameterBlockContainsResidualBlockceres::internal::DynamicProblem417   void ExpectParameterBlockContainsResidualBlock(
418       double* values,
419       ResidualBlock* residual_block) {
420     ParameterBlock* parameter_block =
421         FindOrDie(problem->parameter_map(), values);
422     EXPECT_TRUE(ContainsKey(*(parameter_block->mutable_residual_blocks()),
423                             residual_block));
424   }
425 
ExpectSizeceres::internal::DynamicProblem426   void ExpectSize(double* values, int size) {
427     ParameterBlock* parameter_block =
428         FindOrDie(problem->parameter_map(), values);
429     EXPECT_EQ(size, parameter_block->mutable_residual_blocks()->size());
430   }
431 
432   // Degenerate case.
ExpectParameterBlockContainsceres::internal::DynamicProblem433   void ExpectParameterBlockContains(double* values) {
434     ExpectSize(values, 0);
435   }
436 
ExpectParameterBlockContainsceres::internal::DynamicProblem437   void ExpectParameterBlockContains(double* values,
438                                     ResidualBlock* r1) {
439     ExpectSize(values, 1);
440     ExpectParameterBlockContainsResidualBlock(values, r1);
441   }
442 
ExpectParameterBlockContainsceres::internal::DynamicProblem443   void ExpectParameterBlockContains(double* values,
444                                     ResidualBlock* r1,
445                                     ResidualBlock* r2) {
446     ExpectSize(values, 2);
447     ExpectParameterBlockContainsResidualBlock(values, r1);
448     ExpectParameterBlockContainsResidualBlock(values, r2);
449   }
450 
ExpectParameterBlockContainsceres::internal::DynamicProblem451   void ExpectParameterBlockContains(double* values,
452                                     ResidualBlock* r1,
453                                     ResidualBlock* r2,
454                                     ResidualBlock* r3) {
455     ExpectSize(values, 3);
456     ExpectParameterBlockContainsResidualBlock(values, r1);
457     ExpectParameterBlockContainsResidualBlock(values, r2);
458     ExpectParameterBlockContainsResidualBlock(values, r3);
459   }
460 
ExpectParameterBlockContainsceres::internal::DynamicProblem461   void ExpectParameterBlockContains(double* values,
462                                     ResidualBlock* r1,
463                                     ResidualBlock* r2,
464                                     ResidualBlock* r3,
465                                     ResidualBlock* r4) {
466     ExpectSize(values, 4);
467     ExpectParameterBlockContainsResidualBlock(values, r1);
468     ExpectParameterBlockContainsResidualBlock(values, r2);
469     ExpectParameterBlockContainsResidualBlock(values, r3);
470     ExpectParameterBlockContainsResidualBlock(values, r4);
471   }
472 
473   scoped_ptr<ProblemImpl> problem;
474   double y[4], z[5], w[3];
475 };
476 
TEST(Problem,SetParameterBlockConstantWithUnknownPtrDies)477 TEST(Problem, SetParameterBlockConstantWithUnknownPtrDies) {
478   double x[3];
479   double y[2];
480 
481   Problem problem;
482   problem.AddParameterBlock(x, 3);
483 
484   EXPECT_DEATH_IF_SUPPORTED(problem.SetParameterBlockConstant(y),
485                             "Parameter block not found:");
486 }
487 
TEST(Problem,SetParameterBlockVariableWithUnknownPtrDies)488 TEST(Problem, SetParameterBlockVariableWithUnknownPtrDies) {
489   double x[3];
490   double y[2];
491 
492   Problem problem;
493   problem.AddParameterBlock(x, 3);
494 
495   EXPECT_DEATH_IF_SUPPORTED(problem.SetParameterBlockVariable(y),
496                             "Parameter block not found:");
497 }
498 
TEST(Problem,SetLocalParameterizationWithUnknownPtrDies)499 TEST(Problem, SetLocalParameterizationWithUnknownPtrDies) {
500   double x[3];
501   double y[2];
502 
503   Problem problem;
504   problem.AddParameterBlock(x, 3);
505 
506   EXPECT_DEATH_IF_SUPPORTED(
507       problem.SetParameterization(y, new IdentityParameterization(3)),
508       "Parameter block not found:");
509 }
510 
TEST(Problem,RemoveParameterBlockWithUnknownPtrDies)511 TEST(Problem, RemoveParameterBlockWithUnknownPtrDies) {
512   double x[3];
513   double y[2];
514 
515   Problem problem;
516   problem.AddParameterBlock(x, 3);
517 
518   EXPECT_DEATH_IF_SUPPORTED(
519       problem.RemoveParameterBlock(y), "Parameter block not found:");
520 }
521 
TEST(Problem,GetParameterization)522 TEST(Problem, GetParameterization) {
523   double x[3];
524   double y[2];
525 
526   Problem problem;
527   problem.AddParameterBlock(x, 3);
528   problem.AddParameterBlock(y, 2);
529 
530   LocalParameterization* parameterization =  new IdentityParameterization(3);
531   problem.SetParameterization(x, parameterization);
532   EXPECT_EQ(problem.GetParameterization(x), parameterization);
533   EXPECT_TRUE(problem.GetParameterization(y) == NULL);
534 }
535 
TEST(Problem,ParameterBlockQueryTest)536 TEST(Problem, ParameterBlockQueryTest) {
537   double x[3];
538   double y[4];
539   Problem problem;
540   problem.AddParameterBlock(x, 3);
541   problem.AddParameterBlock(y, 4);
542 
543   vector<int> constant_parameters;
544   constant_parameters.push_back(0);
545   problem.SetParameterization(
546       x,
547       new SubsetParameterization(3, constant_parameters));
548   EXPECT_EQ(problem.ParameterBlockSize(x), 3);
549   EXPECT_EQ(problem.ParameterBlockLocalSize(x), 2);
550   EXPECT_EQ(problem.ParameterBlockLocalSize(y), 4);
551 
552   vector<double*> parameter_blocks;
553   problem.GetParameterBlocks(&parameter_blocks);
554   EXPECT_EQ(parameter_blocks.size(), 2);
555   EXPECT_NE(parameter_blocks[0], parameter_blocks[1]);
556   EXPECT_TRUE(parameter_blocks[0] == x || parameter_blocks[0] == y);
557   EXPECT_TRUE(parameter_blocks[1] == x || parameter_blocks[1] == y);
558 
559   EXPECT_TRUE(problem.HasParameterBlock(x));
560   problem.RemoveParameterBlock(x);
561   EXPECT_FALSE(problem.HasParameterBlock(x));
562   problem.GetParameterBlocks(&parameter_blocks);
563   EXPECT_EQ(parameter_blocks.size(), 1);
564   EXPECT_TRUE(parameter_blocks[0] == y);
565 }
566 
TEST_P(DynamicProblem,RemoveParameterBlockWithNoResiduals)567 TEST_P(DynamicProblem, RemoveParameterBlockWithNoResiduals) {
568   problem->AddParameterBlock(y, 4);
569   problem->AddParameterBlock(z, 5);
570   problem->AddParameterBlock(w, 3);
571   ASSERT_EQ(3, problem->NumParameterBlocks());
572   ASSERT_EQ(0, NumResidualBlocks());
573   EXPECT_EQ(y, GetParameterBlock(0)->user_state());
574   EXPECT_EQ(z, GetParameterBlock(1)->user_state());
575   EXPECT_EQ(w, GetParameterBlock(2)->user_state());
576 
577   // w is at the end, which might break the swapping logic so try adding and
578   // removing it.
579   problem->RemoveParameterBlock(w);
580   ASSERT_EQ(2, problem->NumParameterBlocks());
581   ASSERT_EQ(0, NumResidualBlocks());
582   EXPECT_EQ(y, GetParameterBlock(0)->user_state());
583   EXPECT_EQ(z, GetParameterBlock(1)->user_state());
584   problem->AddParameterBlock(w, 3);
585   ASSERT_EQ(3, problem->NumParameterBlocks());
586   ASSERT_EQ(0, NumResidualBlocks());
587   EXPECT_EQ(y, GetParameterBlock(0)->user_state());
588   EXPECT_EQ(z, GetParameterBlock(1)->user_state());
589   EXPECT_EQ(w, GetParameterBlock(2)->user_state());
590 
591   // Now remove z, which is in the middle, and add it back.
592   problem->RemoveParameterBlock(z);
593   ASSERT_EQ(2, problem->NumParameterBlocks());
594   ASSERT_EQ(0, NumResidualBlocks());
595   EXPECT_EQ(y, GetParameterBlock(0)->user_state());
596   EXPECT_EQ(w, GetParameterBlock(1)->user_state());
597   problem->AddParameterBlock(z, 5);
598   ASSERT_EQ(3, problem->NumParameterBlocks());
599   ASSERT_EQ(0, NumResidualBlocks());
600   EXPECT_EQ(y, GetParameterBlock(0)->user_state());
601   EXPECT_EQ(w, GetParameterBlock(1)->user_state());
602   EXPECT_EQ(z, GetParameterBlock(2)->user_state());
603 
604   // Now remove everything.
605   // y
606   problem->RemoveParameterBlock(y);
607   ASSERT_EQ(2, problem->NumParameterBlocks());
608   ASSERT_EQ(0, NumResidualBlocks());
609   EXPECT_EQ(z, GetParameterBlock(0)->user_state());
610   EXPECT_EQ(w, GetParameterBlock(1)->user_state());
611 
612   // z
613   problem->RemoveParameterBlock(z);
614   ASSERT_EQ(1, problem->NumParameterBlocks());
615   ASSERT_EQ(0, NumResidualBlocks());
616   EXPECT_EQ(w, GetParameterBlock(0)->user_state());
617 
618   // w
619   problem->RemoveParameterBlock(w);
620   EXPECT_EQ(0, problem->NumParameterBlocks());
621   EXPECT_EQ(0, NumResidualBlocks());
622 }
623 
TEST_P(DynamicProblem,RemoveParameterBlockWithResiduals)624 TEST_P(DynamicProblem, RemoveParameterBlockWithResiduals) {
625   problem->AddParameterBlock(y, 4);
626   problem->AddParameterBlock(z, 5);
627   problem->AddParameterBlock(w, 3);
628   ASSERT_EQ(3, problem->NumParameterBlocks());
629   ASSERT_EQ(0, NumResidualBlocks());
630   EXPECT_EQ(y, GetParameterBlock(0)->user_state());
631   EXPECT_EQ(z, GetParameterBlock(1)->user_state());
632   EXPECT_EQ(w, GetParameterBlock(2)->user_state());
633 
634   // Add all combinations of cost functions.
635   CostFunction* cost_yzw = new TernaryCostFunction(1, 4, 5, 3);
636   CostFunction* cost_yz  = new BinaryCostFunction (1, 4, 5);
637   CostFunction* cost_yw  = new BinaryCostFunction (1, 4, 3);
638   CostFunction* cost_zw  = new BinaryCostFunction (1, 5, 3);
639   CostFunction* cost_y   = new UnaryCostFunction  (1, 4);
640   CostFunction* cost_z   = new UnaryCostFunction  (1, 5);
641   CostFunction* cost_w   = new UnaryCostFunction  (1, 3);
642 
643   ResidualBlock* r_yzw = problem->AddResidualBlock(cost_yzw, NULL, y, z, w);
644   ResidualBlock* r_yz  = problem->AddResidualBlock(cost_yz,  NULL, y, z);
645   ResidualBlock* r_yw  = problem->AddResidualBlock(cost_yw,  NULL, y, w);
646   ResidualBlock* r_zw  = problem->AddResidualBlock(cost_zw,  NULL, z, w);
647   ResidualBlock* r_y   = problem->AddResidualBlock(cost_y,   NULL, y);
648   ResidualBlock* r_z   = problem->AddResidualBlock(cost_z,   NULL, z);
649   ResidualBlock* r_w   = problem->AddResidualBlock(cost_w,   NULL, w);
650 
651   EXPECT_EQ(3, problem->NumParameterBlocks());
652   EXPECT_EQ(7, NumResidualBlocks());
653 
654   // Remove w, which should remove r_yzw, r_yw, r_zw, r_w.
655   problem->RemoveParameterBlock(w);
656   ASSERT_EQ(2, problem->NumParameterBlocks());
657   ASSERT_EQ(3, NumResidualBlocks());
658 
659   ASSERT_FALSE(HasResidualBlock(r_yzw));
660   ASSERT_TRUE (HasResidualBlock(r_yz ));
661   ASSERT_FALSE(HasResidualBlock(r_yw ));
662   ASSERT_FALSE(HasResidualBlock(r_zw ));
663   ASSERT_TRUE (HasResidualBlock(r_y  ));
664   ASSERT_TRUE (HasResidualBlock(r_z  ));
665   ASSERT_FALSE(HasResidualBlock(r_w  ));
666 
667   // Remove z, which will remove almost everything else.
668   problem->RemoveParameterBlock(z);
669   ASSERT_EQ(1, problem->NumParameterBlocks());
670   ASSERT_EQ(1, NumResidualBlocks());
671 
672   ASSERT_FALSE(HasResidualBlock(r_yzw));
673   ASSERT_FALSE(HasResidualBlock(r_yz ));
674   ASSERT_FALSE(HasResidualBlock(r_yw ));
675   ASSERT_FALSE(HasResidualBlock(r_zw ));
676   ASSERT_TRUE (HasResidualBlock(r_y  ));
677   ASSERT_FALSE(HasResidualBlock(r_z  ));
678   ASSERT_FALSE(HasResidualBlock(r_w  ));
679 
680   // Remove y; all gone.
681   problem->RemoveParameterBlock(y);
682   EXPECT_EQ(0, problem->NumParameterBlocks());
683   EXPECT_EQ(0, NumResidualBlocks());
684 }
685 
TEST_P(DynamicProblem,RemoveResidualBlock)686 TEST_P(DynamicProblem, RemoveResidualBlock) {
687   problem->AddParameterBlock(y, 4);
688   problem->AddParameterBlock(z, 5);
689   problem->AddParameterBlock(w, 3);
690 
691   // Add all combinations of cost functions.
692   CostFunction* cost_yzw = new TernaryCostFunction(1, 4, 5, 3);
693   CostFunction* cost_yz  = new BinaryCostFunction (1, 4, 5);
694   CostFunction* cost_yw  = new BinaryCostFunction (1, 4, 3);
695   CostFunction* cost_zw  = new BinaryCostFunction (1, 5, 3);
696   CostFunction* cost_y   = new UnaryCostFunction  (1, 4);
697   CostFunction* cost_z   = new UnaryCostFunction  (1, 5);
698   CostFunction* cost_w   = new UnaryCostFunction  (1, 3);
699 
700   ResidualBlock* r_yzw = problem->AddResidualBlock(cost_yzw, NULL, y, z, w);
701   ResidualBlock* r_yz  = problem->AddResidualBlock(cost_yz,  NULL, y, z);
702   ResidualBlock* r_yw  = problem->AddResidualBlock(cost_yw,  NULL, y, w);
703   ResidualBlock* r_zw  = problem->AddResidualBlock(cost_zw,  NULL, z, w);
704   ResidualBlock* r_y   = problem->AddResidualBlock(cost_y,   NULL, y);
705   ResidualBlock* r_z   = problem->AddResidualBlock(cost_z,   NULL, z);
706   ResidualBlock* r_w   = problem->AddResidualBlock(cost_w,   NULL, w);
707 
708   if (GetParam()) {
709     // In this test parameterization, there should be back-pointers from the
710     // parameter blocks to the residual blocks.
711     ExpectParameterBlockContains(y, r_yzw, r_yz, r_yw, r_y);
712     ExpectParameterBlockContains(z, r_yzw, r_yz, r_zw, r_z);
713     ExpectParameterBlockContains(w, r_yzw, r_yw, r_zw, r_w);
714   } else {
715     // Otherwise, nothing.
716     EXPECT_TRUE(GetParameterBlock(0)->mutable_residual_blocks() == NULL);
717     EXPECT_TRUE(GetParameterBlock(1)->mutable_residual_blocks() == NULL);
718     EXPECT_TRUE(GetParameterBlock(2)->mutable_residual_blocks() == NULL);
719   }
720   EXPECT_EQ(3, problem->NumParameterBlocks());
721   EXPECT_EQ(7, NumResidualBlocks());
722 
723   // Remove each residual and check the state after each removal.
724 
725   // Remove r_yzw.
726   problem->RemoveResidualBlock(r_yzw);
727   ASSERT_EQ(3, problem->NumParameterBlocks());
728   ASSERT_EQ(6, NumResidualBlocks());
729   if (GetParam()) {
730     ExpectParameterBlockContains(y, r_yz, r_yw, r_y);
731     ExpectParameterBlockContains(z, r_yz, r_zw, r_z);
732     ExpectParameterBlockContains(w, r_yw, r_zw, r_w);
733   }
734   ASSERT_TRUE (HasResidualBlock(r_yz ));
735   ASSERT_TRUE (HasResidualBlock(r_yw ));
736   ASSERT_TRUE (HasResidualBlock(r_zw ));
737   ASSERT_TRUE (HasResidualBlock(r_y  ));
738   ASSERT_TRUE (HasResidualBlock(r_z  ));
739   ASSERT_TRUE (HasResidualBlock(r_w  ));
740 
741   // Remove r_yw.
742   problem->RemoveResidualBlock(r_yw);
743   ASSERT_EQ(3, problem->NumParameterBlocks());
744   ASSERT_EQ(5, NumResidualBlocks());
745   if (GetParam()) {
746     ExpectParameterBlockContains(y, r_yz, r_y);
747     ExpectParameterBlockContains(z, r_yz, r_zw, r_z);
748     ExpectParameterBlockContains(w, r_zw, r_w);
749   }
750   ASSERT_TRUE (HasResidualBlock(r_yz ));
751   ASSERT_TRUE (HasResidualBlock(r_zw ));
752   ASSERT_TRUE (HasResidualBlock(r_y  ));
753   ASSERT_TRUE (HasResidualBlock(r_z  ));
754   ASSERT_TRUE (HasResidualBlock(r_w  ));
755 
756   // Remove r_zw.
757   problem->RemoveResidualBlock(r_zw);
758   ASSERT_EQ(3, problem->NumParameterBlocks());
759   ASSERT_EQ(4, NumResidualBlocks());
760   if (GetParam()) {
761     ExpectParameterBlockContains(y, r_yz, r_y);
762     ExpectParameterBlockContains(z, r_yz, r_z);
763     ExpectParameterBlockContains(w, r_w);
764   }
765   ASSERT_TRUE (HasResidualBlock(r_yz ));
766   ASSERT_TRUE (HasResidualBlock(r_y  ));
767   ASSERT_TRUE (HasResidualBlock(r_z  ));
768   ASSERT_TRUE (HasResidualBlock(r_w  ));
769 
770   // Remove r_w.
771   problem->RemoveResidualBlock(r_w);
772   ASSERT_EQ(3, problem->NumParameterBlocks());
773   ASSERT_EQ(3, NumResidualBlocks());
774   if (GetParam()) {
775     ExpectParameterBlockContains(y, r_yz, r_y);
776     ExpectParameterBlockContains(z, r_yz, r_z);
777     ExpectParameterBlockContains(w);
778   }
779   ASSERT_TRUE (HasResidualBlock(r_yz ));
780   ASSERT_TRUE (HasResidualBlock(r_y  ));
781   ASSERT_TRUE (HasResidualBlock(r_z  ));
782 
783   // Remove r_yz.
784   problem->RemoveResidualBlock(r_yz);
785   ASSERT_EQ(3, problem->NumParameterBlocks());
786   ASSERT_EQ(2, NumResidualBlocks());
787   if (GetParam()) {
788     ExpectParameterBlockContains(y, r_y);
789     ExpectParameterBlockContains(z, r_z);
790     ExpectParameterBlockContains(w);
791   }
792   ASSERT_TRUE (HasResidualBlock(r_y  ));
793   ASSERT_TRUE (HasResidualBlock(r_z  ));
794 
795   // Remove the last two.
796   problem->RemoveResidualBlock(r_z);
797   problem->RemoveResidualBlock(r_y);
798   ASSERT_EQ(3, problem->NumParameterBlocks());
799   ASSERT_EQ(0, NumResidualBlocks());
800   if (GetParam()) {
801     ExpectParameterBlockContains(y);
802     ExpectParameterBlockContains(z);
803     ExpectParameterBlockContains(w);
804   }
805 }
806 
TEST_P(DynamicProblem,RemoveInvalidResidualBlockDies)807 TEST_P(DynamicProblem, RemoveInvalidResidualBlockDies) {
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   ResidualBlock* r_yz  = problem->AddResidualBlock(cost_yz,  NULL, y, z);
823   ResidualBlock* r_yw  = problem->AddResidualBlock(cost_yw,  NULL, y, w);
824   ResidualBlock* r_zw  = problem->AddResidualBlock(cost_zw,  NULL, z, w);
825   ResidualBlock* r_y   = problem->AddResidualBlock(cost_y,   NULL, y);
826   ResidualBlock* r_z   = problem->AddResidualBlock(cost_z,   NULL, z);
827   ResidualBlock* r_w   = problem->AddResidualBlock(cost_w,   NULL, w);
828 
829   // Remove r_yzw.
830   problem->RemoveResidualBlock(r_yzw);
831   ASSERT_EQ(3, problem->NumParameterBlocks());
832   ASSERT_EQ(6, NumResidualBlocks());
833   // Attempt to remove r_yzw again.
834   EXPECT_DEATH_IF_SUPPORTED(problem->RemoveResidualBlock(r_yzw), "not found");
835 
836   // Attempt to remove a cast pointer never added as a residual.
837   int trash_memory = 1234;
838   ResidualBlock* invalid_residual =
839       reinterpret_cast<ResidualBlock*>(&trash_memory);
840   EXPECT_DEATH_IF_SUPPORTED(problem->RemoveResidualBlock(invalid_residual),
841                             "not found");
842 
843   // Remove a parameter block, which in turn removes the dependent residuals
844   // then attempt to remove them directly.
845   problem->RemoveParameterBlock(z);
846   ASSERT_EQ(2, problem->NumParameterBlocks());
847   ASSERT_EQ(3, NumResidualBlocks());
848   EXPECT_DEATH_IF_SUPPORTED(problem->RemoveResidualBlock(r_yz), "not found");
849   EXPECT_DEATH_IF_SUPPORTED(problem->RemoveResidualBlock(r_zw), "not found");
850   EXPECT_DEATH_IF_SUPPORTED(problem->RemoveResidualBlock(r_z), "not found");
851 
852   problem->RemoveResidualBlock(r_yw);
853   problem->RemoveResidualBlock(r_w);
854   problem->RemoveResidualBlock(r_y);
855 }
856 
857 // Check that a null-terminated array, a, has the same elements as b.
858 template<typename T>
ExpectVectorContainsUnordered(const T * a,const vector<T> & b)859 void ExpectVectorContainsUnordered(const T* a, const vector<T>& b) {
860   // Compute the size of a.
861   int size = 0;
862   while (a[size]) {
863     ++size;
864   }
865   ASSERT_EQ(size, b.size());
866 
867   // Sort a.
868   vector<T> a_sorted(size);
869   copy(a, a + size, a_sorted.begin());
870   sort(a_sorted.begin(), a_sorted.end());
871 
872   // Sort b.
873   vector<T> b_sorted(b);
874   sort(b_sorted.begin(), b_sorted.end());
875 
876   // Compare.
877   for (int i = 0; i < size; ++i) {
878     EXPECT_EQ(a_sorted[i], b_sorted[i]);
879   }
880 }
881 
ExpectProblemHasResidualBlocks(const ProblemImpl & problem,const ResidualBlockId * expected_residual_blocks)882 void ExpectProblemHasResidualBlocks(
883     const ProblemImpl &problem,
884     const ResidualBlockId *expected_residual_blocks) {
885   vector<ResidualBlockId> residual_blocks;
886   problem.GetResidualBlocks(&residual_blocks);
887   ExpectVectorContainsUnordered(expected_residual_blocks, residual_blocks);
888 }
889 
TEST_P(DynamicProblem,GetXXXBlocksForYYYBlock)890 TEST_P(DynamicProblem, GetXXXBlocksForYYYBlock) {
891   problem->AddParameterBlock(y, 4);
892   problem->AddParameterBlock(z, 5);
893   problem->AddParameterBlock(w, 3);
894 
895   // Add all combinations of cost functions.
896   CostFunction* cost_yzw = new TernaryCostFunction(1, 4, 5, 3);
897   CostFunction* cost_yz  = new BinaryCostFunction (1, 4, 5);
898   CostFunction* cost_yw  = new BinaryCostFunction (1, 4, 3);
899   CostFunction* cost_zw  = new BinaryCostFunction (1, 5, 3);
900   CostFunction* cost_y   = new UnaryCostFunction  (1, 4);
901   CostFunction* cost_z   = new UnaryCostFunction  (1, 5);
902   CostFunction* cost_w   = new UnaryCostFunction  (1, 3);
903 
904   ResidualBlock* r_yzw = problem->AddResidualBlock(cost_yzw, NULL, y, z, w);
905   {
906     ResidualBlockId expected_residuals[] = {r_yzw, 0};
907     ExpectProblemHasResidualBlocks(*problem, expected_residuals);
908   }
909   ResidualBlock* r_yz  = problem->AddResidualBlock(cost_yz,  NULL, y, z);
910   {
911     ResidualBlockId expected_residuals[] = {r_yzw, r_yz, 0};
912     ExpectProblemHasResidualBlocks(*problem, expected_residuals);
913   }
914   ResidualBlock* r_yw  = problem->AddResidualBlock(cost_yw,  NULL, y, w);
915   {
916     ResidualBlock *expected_residuals[] = {r_yzw, r_yz, r_yw, 0};
917     ExpectProblemHasResidualBlocks(*problem, expected_residuals);
918   }
919   ResidualBlock* r_zw  = problem->AddResidualBlock(cost_zw,  NULL, z, w);
920   {
921     ResidualBlock *expected_residuals[] = {r_yzw, r_yz, r_yw, r_zw, 0};
922     ExpectProblemHasResidualBlocks(*problem, expected_residuals);
923   }
924   ResidualBlock* r_y   = problem->AddResidualBlock(cost_y,   NULL, y);
925   {
926     ResidualBlock *expected_residuals[] = {r_yzw, r_yz, r_yw, r_zw, r_y, 0};
927     ExpectProblemHasResidualBlocks(*problem, expected_residuals);
928   }
929   ResidualBlock* r_z   = problem->AddResidualBlock(cost_z,   NULL, z);
930   {
931     ResidualBlock *expected_residuals[] = {
932       r_yzw, r_yz, r_yw, r_zw, r_y, r_z, 0
933     };
934     ExpectProblemHasResidualBlocks(*problem, expected_residuals);
935   }
936   ResidualBlock* r_w   = problem->AddResidualBlock(cost_w,   NULL, w);
937   {
938     ResidualBlock *expected_residuals[] = {
939       r_yzw, r_yz, r_yw, r_zw, r_y, r_z, r_w, 0
940     };
941     ExpectProblemHasResidualBlocks(*problem, expected_residuals);
942   }
943 
944   vector<double*> parameter_blocks;
945   vector<ResidualBlockId> residual_blocks;
946 
947   // Check GetResidualBlocksForParameterBlock() for all parameter blocks.
948   struct GetResidualBlocksForParameterBlockTestCase {
949     double* parameter_block;
950     ResidualBlockId expected_residual_blocks[10];
951   };
952   GetResidualBlocksForParameterBlockTestCase get_residual_blocks_cases[] = {
953     { y, { r_yzw, r_yz, r_yw, r_y, NULL} },
954     { z, { r_yzw, r_yz, r_zw, r_z, NULL} },
955     { w, { r_yzw, r_yw, r_zw, r_w, NULL} },
956     { NULL }
957   };
958   for (int i = 0; get_residual_blocks_cases[i].parameter_block; ++i) {
959     problem->GetResidualBlocksForParameterBlock(
960         get_residual_blocks_cases[i].parameter_block,
961         &residual_blocks);
962     ExpectVectorContainsUnordered(
963         get_residual_blocks_cases[i].expected_residual_blocks,
964         residual_blocks);
965   }
966 
967   // Check GetParameterBlocksForResidualBlock() for all residual blocks.
968   struct GetParameterBlocksForResidualBlockTestCase {
969     ResidualBlockId residual_block;
970     double* expected_parameter_blocks[10];
971   };
972   GetParameterBlocksForResidualBlockTestCase get_parameter_blocks_cases[] = {
973     { r_yzw, { y, z, w, NULL } },
974     { r_yz , { y, z, NULL } },
975     { r_yw , { y, w, NULL } },
976     { r_zw , { z, w, NULL } },
977     { r_y  , { y, NULL } },
978     { r_z  , { z, NULL } },
979     { r_w  , { w, NULL } },
980     { NULL }
981   };
982   for (int i = 0; get_parameter_blocks_cases[i].residual_block; ++i) {
983     problem->GetParameterBlocksForResidualBlock(
984         get_parameter_blocks_cases[i].residual_block,
985         &parameter_blocks);
986     ExpectVectorContainsUnordered(
987         get_parameter_blocks_cases[i].expected_parameter_blocks,
988         parameter_blocks);
989   }
990 }
991 
992 INSTANTIATE_TEST_CASE_P(OptionsInstantiation,
993                         DynamicProblem,
994                         ::testing::Values(true, false));
995 
996 // Test for Problem::Evaluate
997 
998 // r_i = i - (j + 1) * x_ij^2
999 template <int kNumResiduals, int kNumParameterBlocks>
1000 class QuadraticCostFunction : public CostFunction {
1001  public:
QuadraticCostFunction()1002   QuadraticCostFunction() {
1003     CHECK_GT(kNumResiduals, 0);
1004     CHECK_GT(kNumParameterBlocks, 0);
1005     set_num_residuals(kNumResiduals);
1006     for (int i = 0; i < kNumParameterBlocks; ++i) {
1007       mutable_parameter_block_sizes()->push_back(kNumResiduals);
1008     }
1009   }
1010 
Evaluate(double const * const * parameters,double * residuals,double ** jacobians) const1011   virtual bool Evaluate(double const* const* parameters,
1012                         double* residuals,
1013                         double** jacobians) const {
1014     for (int i = 0; i < kNumResiduals; ++i) {
1015       residuals[i] = i;
1016       for (int j = 0; j < kNumParameterBlocks; ++j) {
1017         residuals[i] -= (j + 1.0) * parameters[j][i] * parameters[j][i];
1018       }
1019     }
1020 
1021     if (jacobians == NULL) {
1022       return true;
1023     }
1024 
1025     for (int j = 0; j < kNumParameterBlocks; ++j) {
1026       if (jacobians[j] != NULL) {
1027         MatrixRef(jacobians[j], kNumResiduals, kNumResiduals) =
1028             (-2.0 * (j + 1.0) *
1029              ConstVectorRef(parameters[j], kNumResiduals)).asDiagonal();
1030       }
1031     }
1032 
1033     return true;
1034   }
1035 };
1036 
1037 // Convert a CRSMatrix to a dense Eigen matrix.
CRSToDenseMatrix(const CRSMatrix & input,Matrix * output)1038 void CRSToDenseMatrix(const CRSMatrix& input, Matrix* output) {
1039   Matrix& m = *CHECK_NOTNULL(output);
1040   m.resize(input.num_rows, input.num_cols);
1041   m.setZero();
1042   for (int row = 0; row < input.num_rows; ++row) {
1043     for (int j = input.rows[row]; j < input.rows[row + 1]; ++j) {
1044       const int col = input.cols[j];
1045       m(row, col) = input.values[j];
1046     }
1047   }
1048 }
1049 
1050 class ProblemEvaluateTest : public ::testing::Test {
1051  protected:
SetUp()1052   void SetUp() {
1053     for (int i = 0; i < 6; ++i) {
1054       parameters_[i] = static_cast<double>(i + 1);
1055     }
1056 
1057     parameter_blocks_.push_back(parameters_);
1058     parameter_blocks_.push_back(parameters_ + 2);
1059     parameter_blocks_.push_back(parameters_ + 4);
1060 
1061 
1062     CostFunction* cost_function = new QuadraticCostFunction<2, 2>;
1063 
1064     // f(x, y)
1065     residual_blocks_.push_back(
1066         problem_.AddResidualBlock(cost_function,
1067                                   NULL,
1068                                   parameters_,
1069                                   parameters_ + 2));
1070     // g(y, z)
1071     residual_blocks_.push_back(
1072         problem_.AddResidualBlock(cost_function,
1073                                   NULL, parameters_ + 2,
1074                                   parameters_ + 4));
1075     // h(z, x)
1076     residual_blocks_.push_back(
1077         problem_.AddResidualBlock(cost_function,
1078                                   NULL,
1079                                   parameters_ + 4,
1080                                   parameters_));
1081   }
1082 
TearDown()1083   void TearDown() {
1084     EXPECT_TRUE(problem_.program().IsValid());
1085   }
1086 
EvaluateAndCompare(const Problem::EvaluateOptions & options,const int expected_num_rows,const int expected_num_cols,const double expected_cost,const double * expected_residuals,const double * expected_gradient,const double * expected_jacobian)1087   void EvaluateAndCompare(const Problem::EvaluateOptions& options,
1088                           const int expected_num_rows,
1089                           const int expected_num_cols,
1090                           const double expected_cost,
1091                           const double* expected_residuals,
1092                           const double* expected_gradient,
1093                           const double* expected_jacobian) {
1094     double cost;
1095     vector<double> residuals;
1096     vector<double> gradient;
1097     CRSMatrix jacobian;
1098 
1099     EXPECT_TRUE(
1100         problem_.Evaluate(options,
1101                           &cost,
1102                           expected_residuals != NULL ? &residuals : NULL,
1103                           expected_gradient != NULL ? &gradient : NULL,
1104                           expected_jacobian != NULL ? &jacobian : NULL));
1105 
1106     if (expected_residuals != NULL) {
1107       EXPECT_EQ(residuals.size(), expected_num_rows);
1108     }
1109 
1110     if (expected_gradient != NULL) {
1111       EXPECT_EQ(gradient.size(), expected_num_cols);
1112     }
1113 
1114     if (expected_jacobian != NULL) {
1115       EXPECT_EQ(jacobian.num_rows, expected_num_rows);
1116       EXPECT_EQ(jacobian.num_cols, expected_num_cols);
1117     }
1118 
1119     Matrix dense_jacobian;
1120     if (expected_jacobian != NULL) {
1121       CRSToDenseMatrix(jacobian, &dense_jacobian);
1122     }
1123 
1124     CompareEvaluations(expected_num_rows,
1125                        expected_num_cols,
1126                        expected_cost,
1127                        expected_residuals,
1128                        expected_gradient,
1129                        expected_jacobian,
1130                        cost,
1131                        residuals.size() > 0 ? &residuals[0] : NULL,
1132                        gradient.size() > 0 ? &gradient[0] : NULL,
1133                        dense_jacobian.data());
1134   }
1135 
CheckAllEvaluationCombinations(const Problem::EvaluateOptions & options,const ExpectedEvaluation & expected)1136   void CheckAllEvaluationCombinations(const Problem::EvaluateOptions& options,
1137                                       const ExpectedEvaluation& expected) {
1138     for (int i = 0; i < 8; ++i) {
1139       EvaluateAndCompare(options,
1140                          expected.num_rows,
1141                          expected.num_cols,
1142                          expected.cost,
1143                          (i & 1) ? expected.residuals : NULL,
1144                          (i & 2) ? expected.gradient  : NULL,
1145                          (i & 4) ? expected.jacobian  : NULL);
1146     }
1147   }
1148 
1149   ProblemImpl problem_;
1150   double parameters_[6];
1151   vector<double*> parameter_blocks_;
1152   vector<ResidualBlockId> residual_blocks_;
1153 };
1154 
1155 
TEST_F(ProblemEvaluateTest,MultipleParameterAndResidualBlocks)1156 TEST_F(ProblemEvaluateTest, MultipleParameterAndResidualBlocks) {
1157   ExpectedEvaluation expected = {
1158     // Rows/columns
1159     6, 6,
1160     // Cost
1161     7607.0,
1162     // Residuals
1163     { -19.0, -35.0,  // f
1164       -59.0, -87.0,  // g
1165       -27.0, -43.0   // h
1166     },
1167     // Gradient
1168     {  146.0,  484.0,   // x
1169        582.0, 1256.0,   // y
1170       1450.0, 2604.0,   // z
1171     },
1172     // Jacobian
1173     //                       x             y             z
1174     { /* f(x, y) */ -2.0,  0.0, -12.0,   0.0,   0.0,   0.0,
1175                      0.0, -4.0,   0.0, -16.0,   0.0,   0.0,
1176       /* g(y, z) */  0.0,  0.0,  -6.0,   0.0, -20.0,   0.0,
1177                      0.0,  0.0,   0.0,  -8.0,   0.0, -24.0,
1178       /* h(z, x) */ -4.0,  0.0,   0.0,   0.0, -10.0,   0.0,
1179                      0.0, -8.0,   0.0,   0.0,   0.0, -12.0
1180     }
1181   };
1182 
1183   CheckAllEvaluationCombinations(Problem::EvaluateOptions(), expected);
1184 }
1185 
TEST_F(ProblemEvaluateTest,ParameterAndResidualBlocksPassedInOptions)1186 TEST_F(ProblemEvaluateTest, ParameterAndResidualBlocksPassedInOptions) {
1187   ExpectedEvaluation expected = {
1188     // Rows/columns
1189     6, 6,
1190     // Cost
1191     7607.0,
1192     // Residuals
1193     { -19.0, -35.0,  // f
1194       -59.0, -87.0,  // g
1195       -27.0, -43.0   // h
1196     },
1197     // Gradient
1198     {  146.0,  484.0,   // x
1199        582.0, 1256.0,   // y
1200       1450.0, 2604.0,   // z
1201     },
1202     // Jacobian
1203     //                       x             y             z
1204     { /* f(x, y) */ -2.0,  0.0, -12.0,   0.0,   0.0,   0.0,
1205                      0.0, -4.0,   0.0, -16.0,   0.0,   0.0,
1206       /* g(y, z) */  0.0,  0.0,  -6.0,   0.0, -20.0,   0.0,
1207                      0.0,  0.0,   0.0,  -8.0,   0.0, -24.0,
1208       /* h(z, x) */ -4.0,  0.0,   0.0,   0.0, -10.0,   0.0,
1209                      0.0, -8.0,   0.0,   0.0,   0.0, -12.0
1210     }
1211   };
1212 
1213   Problem::EvaluateOptions evaluate_options;
1214   evaluate_options.parameter_blocks = parameter_blocks_;
1215   evaluate_options.residual_blocks = residual_blocks_;
1216   CheckAllEvaluationCombinations(evaluate_options, expected);
1217 }
1218 
TEST_F(ProblemEvaluateTest,ReorderedResidualBlocks)1219 TEST_F(ProblemEvaluateTest, ReorderedResidualBlocks) {
1220   ExpectedEvaluation expected = {
1221     // Rows/columns
1222     6, 6,
1223     // Cost
1224     7607.0,
1225     // Residuals
1226     { -19.0, -35.0,  // f
1227       -27.0, -43.0,  // h
1228       -59.0, -87.0   // g
1229     },
1230     // Gradient
1231     {  146.0,  484.0,   // x
1232        582.0, 1256.0,   // y
1233       1450.0, 2604.0,   // z
1234     },
1235     // Jacobian
1236     //                       x             y             z
1237     { /* f(x, y) */ -2.0,  0.0, -12.0,   0.0,   0.0,   0.0,
1238                      0.0, -4.0,   0.0, -16.0,   0.0,   0.0,
1239       /* h(z, x) */ -4.0,  0.0,   0.0,   0.0, -10.0,   0.0,
1240                      0.0, -8.0,   0.0,   0.0,   0.0, -12.0,
1241       /* g(y, z) */  0.0,  0.0,  -6.0,   0.0, -20.0,   0.0,
1242                      0.0,  0.0,   0.0,  -8.0,   0.0, -24.0
1243     }
1244   };
1245 
1246   Problem::EvaluateOptions evaluate_options;
1247   evaluate_options.parameter_blocks = parameter_blocks_;
1248 
1249   // f, h, g
1250   evaluate_options.residual_blocks.push_back(residual_blocks_[0]);
1251   evaluate_options.residual_blocks.push_back(residual_blocks_[2]);
1252   evaluate_options.residual_blocks.push_back(residual_blocks_[1]);
1253 
1254   CheckAllEvaluationCombinations(evaluate_options, expected);
1255 }
1256 
TEST_F(ProblemEvaluateTest,ReorderedResidualBlocksAndReorderedParameterBlocks)1257 TEST_F(ProblemEvaluateTest, ReorderedResidualBlocksAndReorderedParameterBlocks) {
1258   ExpectedEvaluation expected = {
1259     // Rows/columns
1260     6, 6,
1261     // Cost
1262     7607.0,
1263     // Residuals
1264     { -19.0, -35.0,  // f
1265       -27.0, -43.0,  // h
1266       -59.0, -87.0   // g
1267     },
1268     // Gradient
1269     {  1450.0, 2604.0,   // z
1270         582.0, 1256.0,   // y
1271         146.0,  484.0,   // x
1272     },
1273      // Jacobian
1274     //                       z             y             x
1275     { /* f(x, y) */   0.0,   0.0, -12.0,   0.0,  -2.0,   0.0,
1276                       0.0,   0.0,   0.0, -16.0,   0.0,  -4.0,
1277       /* h(z, x) */ -10.0,   0.0,   0.0,   0.0,  -4.0,   0.0,
1278                       0.0, -12.0,   0.0,   0.0,   0.0,  -8.0,
1279       /* g(y, z) */ -20.0,   0.0,  -6.0,   0.0,   0.0,   0.0,
1280                       0.0, -24.0,   0.0,  -8.0,   0.0,   0.0
1281     }
1282   };
1283 
1284   Problem::EvaluateOptions evaluate_options;
1285   // z, y, x
1286   evaluate_options.parameter_blocks.push_back(parameter_blocks_[2]);
1287   evaluate_options.parameter_blocks.push_back(parameter_blocks_[1]);
1288   evaluate_options.parameter_blocks.push_back(parameter_blocks_[0]);
1289 
1290   // f, h, g
1291   evaluate_options.residual_blocks.push_back(residual_blocks_[0]);
1292   evaluate_options.residual_blocks.push_back(residual_blocks_[2]);
1293   evaluate_options.residual_blocks.push_back(residual_blocks_[1]);
1294 
1295   CheckAllEvaluationCombinations(evaluate_options, expected);
1296 }
1297 
TEST_F(ProblemEvaluateTest,ConstantParameterBlock)1298 TEST_F(ProblemEvaluateTest, ConstantParameterBlock) {
1299   ExpectedEvaluation expected = {
1300     // Rows/columns
1301     6, 6,
1302     // Cost
1303     7607.0,
1304     // Residuals
1305     { -19.0, -35.0,  // f
1306       -59.0, -87.0,  // g
1307       -27.0, -43.0   // h
1308     },
1309 
1310     // Gradient
1311     {  146.0,  484.0,  // x
1312          0.0,    0.0,  // y
1313       1450.0, 2604.0,  // z
1314     },
1315 
1316     // Jacobian
1317     //                       x             y             z
1318     { /* f(x, y) */ -2.0,  0.0,   0.0,   0.0,   0.0,   0.0,
1319                      0.0, -4.0,   0.0,   0.0,   0.0,   0.0,
1320       /* g(y, z) */  0.0,  0.0,   0.0,   0.0, -20.0,   0.0,
1321                      0.0,  0.0,   0.0,   0.0,   0.0, -24.0,
1322       /* h(z, x) */ -4.0,  0.0,   0.0,   0.0, -10.0,   0.0,
1323                      0.0, -8.0,   0.0,   0.0,   0.0, -12.0
1324     }
1325   };
1326 
1327   problem_.SetParameterBlockConstant(parameters_ + 2);
1328   CheckAllEvaluationCombinations(Problem::EvaluateOptions(), expected);
1329 }
1330 
TEST_F(ProblemEvaluateTest,ExcludedAResidualBlock)1331 TEST_F(ProblemEvaluateTest, ExcludedAResidualBlock) {
1332   ExpectedEvaluation expected = {
1333     // Rows/columns
1334     4, 6,
1335     // Cost
1336     2082.0,
1337     // Residuals
1338     { -19.0, -35.0,  // f
1339       -27.0, -43.0   // h
1340     },
1341     // Gradient
1342     {  146.0,  484.0,   // x
1343        228.0,  560.0,   // y
1344        270.0,  516.0,   // z
1345     },
1346     // Jacobian
1347     //                       x             y             z
1348     { /* f(x, y) */ -2.0,  0.0, -12.0,   0.0,   0.0,   0.0,
1349                      0.0, -4.0,   0.0, -16.0,   0.0,   0.0,
1350       /* h(z, x) */ -4.0,  0.0,   0.0,   0.0, -10.0,   0.0,
1351                      0.0, -8.0,   0.0,   0.0,   0.0, -12.0
1352     }
1353   };
1354 
1355   Problem::EvaluateOptions evaluate_options;
1356   evaluate_options.residual_blocks.push_back(residual_blocks_[0]);
1357   evaluate_options.residual_blocks.push_back(residual_blocks_[2]);
1358 
1359   CheckAllEvaluationCombinations(evaluate_options, expected);
1360 }
1361 
TEST_F(ProblemEvaluateTest,ExcludedParameterBlock)1362 TEST_F(ProblemEvaluateTest, ExcludedParameterBlock) {
1363   ExpectedEvaluation expected = {
1364     // Rows/columns
1365     6, 4,
1366     // Cost
1367     7607.0,
1368     // Residuals
1369     { -19.0, -35.0,  // f
1370       -59.0, -87.0,  // g
1371       -27.0, -43.0   // h
1372     },
1373 
1374     // Gradient
1375     {  146.0,  484.0,  // x
1376       1450.0, 2604.0,  // z
1377     },
1378 
1379     // Jacobian
1380     //                       x             z
1381     { /* f(x, y) */ -2.0,  0.0,   0.0,   0.0,
1382                      0.0, -4.0,   0.0,   0.0,
1383       /* g(y, z) */  0.0,  0.0, -20.0,   0.0,
1384                      0.0,  0.0,   0.0, -24.0,
1385       /* h(z, x) */ -4.0,  0.0, -10.0,   0.0,
1386                      0.0, -8.0,   0.0, -12.0
1387     }
1388   };
1389 
1390   Problem::EvaluateOptions evaluate_options;
1391   // x, z
1392   evaluate_options.parameter_blocks.push_back(parameter_blocks_[0]);
1393   evaluate_options.parameter_blocks.push_back(parameter_blocks_[2]);
1394   evaluate_options.residual_blocks = residual_blocks_;
1395   CheckAllEvaluationCombinations(evaluate_options, expected);
1396 }
1397 
TEST_F(ProblemEvaluateTest,ExcludedParameterBlockAndExcludedResidualBlock)1398 TEST_F(ProblemEvaluateTest, ExcludedParameterBlockAndExcludedResidualBlock) {
1399   ExpectedEvaluation expected = {
1400     // Rows/columns
1401     4, 4,
1402     // Cost
1403     6318.0,
1404     // Residuals
1405     { -19.0, -35.0,  // f
1406       -59.0, -87.0,  // g
1407     },
1408 
1409     // Gradient
1410     {   38.0,  140.0,  // x
1411       1180.0, 2088.0,  // z
1412     },
1413 
1414     // Jacobian
1415     //                       x             z
1416     { /* f(x, y) */ -2.0,  0.0,   0.0,   0.0,
1417                      0.0, -4.0,   0.0,   0.0,
1418       /* g(y, z) */  0.0,  0.0, -20.0,   0.0,
1419                      0.0,  0.0,   0.0, -24.0,
1420     }
1421   };
1422 
1423   Problem::EvaluateOptions evaluate_options;
1424   // x, z
1425   evaluate_options.parameter_blocks.push_back(parameter_blocks_[0]);
1426   evaluate_options.parameter_blocks.push_back(parameter_blocks_[2]);
1427   evaluate_options.residual_blocks.push_back(residual_blocks_[0]);
1428   evaluate_options.residual_blocks.push_back(residual_blocks_[1]);
1429 
1430   CheckAllEvaluationCombinations(evaluate_options, expected);
1431 }
1432 
TEST_F(ProblemEvaluateTest,LocalParameterization)1433 TEST_F(ProblemEvaluateTest, LocalParameterization) {
1434   ExpectedEvaluation expected = {
1435     // Rows/columns
1436     6, 5,
1437     // Cost
1438     7607.0,
1439     // Residuals
1440     { -19.0, -35.0,  // f
1441       -59.0, -87.0,  // g
1442       -27.0, -43.0   // h
1443     },
1444     // Gradient
1445     {  146.0,  484.0,  // x
1446       1256.0,          // y with SubsetParameterization
1447       1450.0, 2604.0,  // z
1448     },
1449     // Jacobian
1450     //                       x      y             z
1451     { /* f(x, y) */ -2.0,  0.0,   0.0,   0.0,   0.0,
1452                      0.0, -4.0, -16.0,   0.0,   0.0,
1453       /* g(y, z) */  0.0,  0.0,   0.0, -20.0,   0.0,
1454                      0.0,  0.0,  -8.0,   0.0, -24.0,
1455       /* h(z, x) */ -4.0,  0.0,   0.0, -10.0,   0.0,
1456                      0.0, -8.0,   0.0,   0.0, -12.0
1457     }
1458   };
1459 
1460   vector<int> constant_parameters;
1461   constant_parameters.push_back(0);
1462   problem_.SetParameterization(parameters_ + 2,
1463                                new SubsetParameterization(2,
1464                                                           constant_parameters));
1465 
1466   CheckAllEvaluationCombinations(Problem::EvaluateOptions(), expected);
1467 }
1468 
1469 }  // namespace internal
1470 }  // namespace ceres
1471