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
50namespace 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, int32 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                     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
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                      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
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
127  EXPECT_DEATH_IF_SUPPORTED(problem.AddResidualBlock(NULL, NULL, x),
128                            "'cost_function' Must be non NULL");
129}
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.
140  EXPECT_DEATH_IF_SUPPORTED(
141      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);
150  EXPECT_DEATH_IF_SUPPORTED(problem.AddResidualBlock(
151                                new UnaryCostFunction(
152                                    2, 4 /* 4 != 3 */), NULL, x),
153                            "different block sizes");
154}
155
156TEST(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
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.
179  EXPECT_DEATH_IF_SUPPORTED(problem.AddResidualBlock(
180      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
204  EXPECT_DEATH_IF_SUPPORTED(problem.AddParameterBlock(x, 4),
205                            "different block sizes");
206}
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
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
248TEST(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.
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
304class DestructorCountingCostFunction : public SizedCostFunction<3, 4, 5> {
305 public:
306  explicit DestructorCountingCostFunction(int *num_destructions)
307      : num_destructions_(num_destructions) {}
308
309  virtual ~DestructorCountingCostFunction() {
310    *num_destructions_ += 1;
311  }
312
313  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
323TEST(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
345TEST(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_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    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
406  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.
417  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
426  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.
433  void ExpectParameterBlockContains(double* values) {
434    ExpectSize(values, 0);
435  }
436
437  void ExpectParameterBlockContains(double* values,
438                                    ResidualBlock* r1) {
439    ExpectSize(values, 1);
440    ExpectParameterBlockContainsResidualBlock(values, r1);
441  }
442
443  void ExpectParameterBlockContains(double* values,
444                                    ResidualBlock* r1,
445                                    ResidualBlock* r2) {
446    ExpectSize(values, 2);
447    ExpectParameterBlockContainsResidualBlock(values, r1);
448    ExpectParameterBlockContainsResidualBlock(values, r2);
449  }
450
451  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
461  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
477TEST(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
488TEST(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
499TEST(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
511TEST(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
522TEST(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
536TEST(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
567TEST_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
624TEST_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
686TEST_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
807TEST_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.
858template<typename T>
859void 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
882void 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
890TEST_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
992INSTANTIATE_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
999template <int kNumResiduals, int kNumParameterBlocks>
1000class QuadraticCostFunction : public CostFunction {
1001 public:
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
1011  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.
1038void 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
1050class ProblemEvaluateTest : public ::testing::Test {
1051 protected:
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
1083  void TearDown() {
1084    EXPECT_TRUE(problem_.program().IsValid());
1085  }
1086
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
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
1156TEST_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
1186TEST_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
1219TEST_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
1257TEST_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
1298TEST_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
1331TEST_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
1362TEST_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
1398TEST_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
1433TEST_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