| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585 | // Ceres Solver - A fast non-linear least squares minimizer// Copyright 2015 Google Inc. All rights reserved.// http://ceres-solver.org///// Redistribution and use in source and binary forms, with or without// modification, are permitted provided that the following conditions are met://// * Redistributions of source code must retain the above copyright notice,//   this list of conditions and the following disclaimer.// * Redistributions in binary form must reproduce the above copyright notice,//   this list of conditions and the following disclaimer in the documentation//   and/or other materials provided with the distribution.// * Neither the name of Google Inc. nor the names of its contributors may be//   used to endorse or promote products derived from this software without//   specific prior written permission.//// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE// POSSIBILITY OF SUCH DAMAGE.//// Author: sameeragarwal@google.com (Sameer Agarwal)//         keir@google.com (Keir Mierle)#include "ceres/problem.h"#include "ceres/problem_impl.h"#include <memory>#include "ceres/casts.h"#include "ceres/cost_function.h"#include "ceres/crs_matrix.h"#include "ceres/evaluator_test_utils.h"#include "ceres/internal/eigen.h"#include "ceres/local_parameterization.h"#include "ceres/loss_function.h"#include "ceres/map_util.h"#include "ceres/parameter_block.h"#include "ceres/program.h"#include "ceres/sized_cost_function.h"#include "ceres/sparse_matrix.h"#include "ceres/types.h"#include "gtest/gtest.h"namespace ceres {namespace internal {using std::vector;// The following three classes are for the purposes of defining// function signatures. They have dummy Evaluate functions.// Trivial cost function that accepts a single argument.class UnaryCostFunction : public CostFunction { public:  UnaryCostFunction(int num_residuals, int32 parameter_block_size) {    set_num_residuals(num_residuals);    mutable_parameter_block_sizes()->push_back(parameter_block_size);  }  virtual ~UnaryCostFunction() {}  virtual bool Evaluate(double const* const* parameters,                        double* residuals,                        double** jacobians) const {    for (int i = 0; i < num_residuals(); ++i) {      residuals[i] = 1;    }    return true;  }};// Trivial cost function that accepts two arguments.class BinaryCostFunction: public CostFunction { public:  BinaryCostFunction(int num_residuals,                     int32 parameter_block1_size,                     int32 parameter_block2_size) {    set_num_residuals(num_residuals);    mutable_parameter_block_sizes()->push_back(parameter_block1_size);    mutable_parameter_block_sizes()->push_back(parameter_block2_size);  }  virtual bool Evaluate(double const* const* parameters,                        double* residuals,                        double** jacobians) const {    for (int i = 0; i < num_residuals(); ++i) {      residuals[i] = 2;    }    return true;  }};// Trivial cost function that accepts three arguments.class TernaryCostFunction: public CostFunction { public:  TernaryCostFunction(int num_residuals,                      int32 parameter_block1_size,                      int32 parameter_block2_size,                      int32 parameter_block3_size) {    set_num_residuals(num_residuals);    mutable_parameter_block_sizes()->push_back(parameter_block1_size);    mutable_parameter_block_sizes()->push_back(parameter_block2_size);    mutable_parameter_block_sizes()->push_back(parameter_block3_size);  }  virtual bool Evaluate(double const* const* parameters,                        double* residuals,                        double** jacobians) const {    for (int i = 0; i < num_residuals(); ++i) {      residuals[i] = 3;    }    return true;  }};TEST(Problem, AddResidualWithNullCostFunctionDies) {  double x[3], y[4], z[5];  Problem problem;  problem.AddParameterBlock(x, 3);  problem.AddParameterBlock(y, 4);  problem.AddParameterBlock(z, 5);  EXPECT_DEATH_IF_SUPPORTED(problem.AddResidualBlock(NULL, NULL, x),                            "'cost_function' Must be non NULL");}TEST(Problem, AddResidualWithIncorrectNumberOfParameterBlocksDies) {  double x[3], y[4], z[5];  Problem problem;  problem.AddParameterBlock(x, 3);  problem.AddParameterBlock(y, 4);  problem.AddParameterBlock(z, 5);  // UnaryCostFunction takes only one parameter, but two are passed.  EXPECT_DEATH_IF_SUPPORTED(      problem.AddResidualBlock(new UnaryCostFunction(2, 3), NULL, x, y),      "parameter_blocks.size");}TEST(Problem, AddResidualWithDifferentSizesOnTheSameVariableDies) {  double x[3];  Problem problem;  problem.AddResidualBlock(new UnaryCostFunction(2, 3), NULL, x);  EXPECT_DEATH_IF_SUPPORTED(problem.AddResidualBlock(                                new UnaryCostFunction(                                    2, 4 /* 4 != 3 */), NULL, x),                            "different block sizes");}TEST(Problem, AddResidualWithDuplicateParametersDies) {  double x[3], z[5];  Problem problem;  EXPECT_DEATH_IF_SUPPORTED(problem.AddResidualBlock(                                new BinaryCostFunction(2, 3, 3), NULL, x, x),                            "Duplicate parameter blocks");  EXPECT_DEATH_IF_SUPPORTED(problem.AddResidualBlock(                                new TernaryCostFunction(1, 5, 3, 5),                                NULL, z, x, z),                            "Duplicate parameter blocks");}TEST(Problem, AddResidualWithIncorrectSizesOfParameterBlockDies) {  double x[3], y[4], z[5];  Problem problem;  problem.AddParameterBlock(x, 3);  problem.AddParameterBlock(y, 4);  problem.AddParameterBlock(z, 5);  // The cost function expects the size of the second parameter, z, to be 4  // instead of 5 as declared above. This is fatal.  EXPECT_DEATH_IF_SUPPORTED(problem.AddResidualBlock(      new BinaryCostFunction(2, 3, 4), NULL, x, z),               "different block sizes");}TEST(Problem, AddResidualAddsDuplicatedParametersOnlyOnce) {  double x[3], y[4], z[5];  Problem problem;  problem.AddResidualBlock(new UnaryCostFunction(2, 3), NULL, x);  problem.AddResidualBlock(new UnaryCostFunction(2, 3), NULL, x);  problem.AddResidualBlock(new UnaryCostFunction(2, 4), NULL, y);  problem.AddResidualBlock(new UnaryCostFunction(2, 5), NULL, z);  EXPECT_EQ(3, problem.NumParameterBlocks());  EXPECT_EQ(12, problem.NumParameters());}TEST(Problem, AddParameterWithDifferentSizesOnTheSameVariableDies) {  double x[3], y[4];  Problem problem;  problem.AddParameterBlock(x, 3);  problem.AddParameterBlock(y, 4);  EXPECT_DEATH_IF_SUPPORTED(problem.AddParameterBlock(x, 4),                            "different block sizes");}static double *IntToPtr(int i) {  return reinterpret_cast<double*>(sizeof(double) * i);  // NOLINT}TEST(Problem, AddParameterWithAliasedParametersDies) {  // Layout is  //  //   0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17  //                 [x] x  x  x  x          [y] y  y  //         o==o==o                 o==o==o           o==o  //               o--o--o     o--o--o     o--o  o--o--o  //  // Parameter block additions are tested as listed above; expected successful  // ones marked with o==o and aliasing ones marked with o--o.  Problem problem;  problem.AddParameterBlock(IntToPtr(5),  5);  // x  problem.AddParameterBlock(IntToPtr(13), 3);  // y  EXPECT_DEATH_IF_SUPPORTED(problem.AddParameterBlock(IntToPtr( 4), 2),                            "Aliasing detected");  EXPECT_DEATH_IF_SUPPORTED(problem.AddParameterBlock(IntToPtr( 4), 3),                            "Aliasing detected");  EXPECT_DEATH_IF_SUPPORTED(problem.AddParameterBlock(IntToPtr( 4), 9),                            "Aliasing detected");  EXPECT_DEATH_IF_SUPPORTED(problem.AddParameterBlock(IntToPtr( 8), 3),                            "Aliasing detected");  EXPECT_DEATH_IF_SUPPORTED(problem.AddParameterBlock(IntToPtr(12), 2),                            "Aliasing detected");  EXPECT_DEATH_IF_SUPPORTED(problem.AddParameterBlock(IntToPtr(14), 3),                            "Aliasing detected");  // These ones should work.  problem.AddParameterBlock(IntToPtr( 2), 3);  problem.AddParameterBlock(IntToPtr(10), 3);  problem.AddParameterBlock(IntToPtr(16), 2);  ASSERT_EQ(5, problem.NumParameterBlocks());}TEST(Problem, AddParameterIgnoresDuplicateCalls) {  double x[3], y[4];  Problem problem;  problem.AddParameterBlock(x, 3);  problem.AddParameterBlock(y, 4);  // Creating parameter blocks multiple times is ignored.  problem.AddParameterBlock(x, 3);  problem.AddResidualBlock(new UnaryCostFunction(2, 3), NULL, x);  // ... even repeatedly.  problem.AddParameterBlock(x, 3);  problem.AddResidualBlock(new UnaryCostFunction(2, 3), NULL, x);  // More parameters are fine.  problem.AddParameterBlock(y, 4);  problem.AddResidualBlock(new UnaryCostFunction(2, 4), NULL, y);  EXPECT_EQ(2, problem.NumParameterBlocks());  EXPECT_EQ(7, problem.NumParameters());}TEST(Problem, AddingParametersAndResidualsResultsInExpectedProblem) {  double x[3], y[4], z[5], w[4];  Problem problem;  problem.AddParameterBlock(x, 3);  EXPECT_EQ(1, problem.NumParameterBlocks());  EXPECT_EQ(3, problem.NumParameters());  problem.AddParameterBlock(y, 4);  EXPECT_EQ(2, problem.NumParameterBlocks());  EXPECT_EQ(7, problem.NumParameters());  problem.AddParameterBlock(z, 5);  EXPECT_EQ(3,  problem.NumParameterBlocks());  EXPECT_EQ(12, problem.NumParameters());  // Add a parameter that has a local parameterization.  w[0] = 1.0; w[1] = 0.0; w[2] = 0.0; w[3] = 0.0;  problem.AddParameterBlock(w, 4, new QuaternionParameterization);  EXPECT_EQ(4,  problem.NumParameterBlocks());  EXPECT_EQ(16, problem.NumParameters());  problem.AddResidualBlock(new UnaryCostFunction(2, 3), NULL, x);  problem.AddResidualBlock(new BinaryCostFunction(6, 5, 4) , NULL, z, y);  problem.AddResidualBlock(new BinaryCostFunction(3, 3, 5), NULL, x, z);  problem.AddResidualBlock(new BinaryCostFunction(7, 5, 3), NULL, z, x);  problem.AddResidualBlock(new TernaryCostFunction(1, 5, 3, 4), NULL, z, x, y);  const int total_residuals = 2 + 6 + 3 + 7 + 1;  EXPECT_EQ(problem.NumResidualBlocks(), 5);  EXPECT_EQ(problem.NumResiduals(), total_residuals);}class DestructorCountingCostFunction : public SizedCostFunction<3, 4, 5> { public:  explicit DestructorCountingCostFunction(int *num_destructions)      : num_destructions_(num_destructions) {}  virtual ~DestructorCountingCostFunction() {    *num_destructions_ += 1;  }  virtual bool Evaluate(double const* const* parameters,                        double* residuals,                        double** jacobians) const {    return true;  } private:  int* num_destructions_;};TEST(Problem, ReusedCostFunctionsAreOnlyDeletedOnce) {  double y[4], z[5];  int num_destructions = 0;  // Add a cost function multiple times and check to make sure that  // the destructor on the cost function is only called once.  {    Problem problem;    problem.AddParameterBlock(y, 4);    problem.AddParameterBlock(z, 5);    CostFunction* cost = new DestructorCountingCostFunction(&num_destructions);    problem.AddResidualBlock(cost, NULL, y, z);    problem.AddResidualBlock(cost, NULL, y, z);    problem.AddResidualBlock(cost, NULL, y, z);    EXPECT_EQ(3, problem.NumResidualBlocks());  }  // Check that the destructor was called only once.  CHECK_EQ(num_destructions, 1);}TEST(Problem, GetCostFunctionForResidualBlock) {  double x[3];  Problem problem;  CostFunction* cost_function = new UnaryCostFunction(2, 3);  const ResidualBlockId residual_block =      problem.AddResidualBlock(cost_function, NULL, x);  EXPECT_EQ(problem.GetCostFunctionForResidualBlock(residual_block),            cost_function);  EXPECT_TRUE(problem.GetLossFunctionForResidualBlock(residual_block) == NULL);}TEST(Problem, GetLossFunctionForResidualBlock) {  double x[3];  Problem problem;  CostFunction* cost_function = new UnaryCostFunction(2, 3);  LossFunction* loss_function = new TrivialLoss();  const ResidualBlockId residual_block =      problem.AddResidualBlock(cost_function, loss_function, x);  EXPECT_EQ(problem.GetCostFunctionForResidualBlock(residual_block),            cost_function);  EXPECT_EQ(problem.GetLossFunctionForResidualBlock(residual_block),            loss_function);}TEST(Problem, CostFunctionsAreDeletedEvenWithRemovals) {  double y[4], z[5], w[4];  int num_destructions = 0;  {    Problem problem;    problem.AddParameterBlock(y, 4);    problem.AddParameterBlock(z, 5);    CostFunction* cost_yz =        new DestructorCountingCostFunction(&num_destructions);    CostFunction* cost_wz =        new DestructorCountingCostFunction(&num_destructions);    ResidualBlock* r_yz = problem.AddResidualBlock(cost_yz, NULL, y, z);    ResidualBlock* r_wz = problem.AddResidualBlock(cost_wz, NULL, w, z);    EXPECT_EQ(2, problem.NumResidualBlocks());    problem.RemoveResidualBlock(r_yz);    CHECK_EQ(num_destructions, 1);    problem.RemoveResidualBlock(r_wz);    CHECK_EQ(num_destructions, 2);    EXPECT_EQ(0, problem.NumResidualBlocks());  }  CHECK_EQ(num_destructions, 2);}// Make the dynamic problem tests (e.g. for removing residual blocks)// parameterized on whether the low-latency mode is enabled or not.//// This tests against ProblemImpl instead of Problem in order to inspect the// state of the resulting Program; this is difficult with only the thin Problem// interface.struct DynamicProblem : public ::testing::TestWithParam<bool> {  DynamicProblem() {    Problem::Options options;    options.enable_fast_removal = GetParam();    problem.reset(new ProblemImpl(options));  }  ParameterBlock* GetParameterBlock(int block) {    return problem->program().parameter_blocks()[block];  }  ResidualBlock* GetResidualBlock(int block) {    return problem->program().residual_blocks()[block];  }  bool HasResidualBlock(ResidualBlock* residual_block) {    bool have_residual_block = true;    if (GetParam()) {      have_residual_block &=          (problem->residual_block_set().find(residual_block) !=           problem->residual_block_set().end());    }    have_residual_block &=        find(problem->program().residual_blocks().begin(),             problem->program().residual_blocks().end(),             residual_block) != problem->program().residual_blocks().end();    return have_residual_block;  }  int NumResidualBlocks() {    // Verify that the hash set of residuals is maintained consistently.    if (GetParam()) {      EXPECT_EQ(problem->residual_block_set().size(),                problem->NumResidualBlocks());    }    return problem->NumResidualBlocks();  }  // The next block of functions until the end are only for testing the  // residual block removals.  void ExpectParameterBlockContainsResidualBlock(      double* values,      ResidualBlock* residual_block) {    ParameterBlock* parameter_block =        FindOrDie(problem->parameter_map(), values);    EXPECT_TRUE(ContainsKey(*(parameter_block->mutable_residual_blocks()),                            residual_block));  }  void ExpectSize(double* values, int size) {    ParameterBlock* parameter_block =        FindOrDie(problem->parameter_map(), values);    EXPECT_EQ(size, parameter_block->mutable_residual_blocks()->size());  }  // Degenerate case.  void ExpectParameterBlockContains(double* values) {    ExpectSize(values, 0);  }  void ExpectParameterBlockContains(double* values,                                    ResidualBlock* r1) {    ExpectSize(values, 1);    ExpectParameterBlockContainsResidualBlock(values, r1);  }  void ExpectParameterBlockContains(double* values,                                    ResidualBlock* r1,                                    ResidualBlock* r2) {    ExpectSize(values, 2);    ExpectParameterBlockContainsResidualBlock(values, r1);    ExpectParameterBlockContainsResidualBlock(values, r2);  }  void ExpectParameterBlockContains(double* values,                                    ResidualBlock* r1,                                    ResidualBlock* r2,                                    ResidualBlock* r3) {    ExpectSize(values, 3);    ExpectParameterBlockContainsResidualBlock(values, r1);    ExpectParameterBlockContainsResidualBlock(values, r2);    ExpectParameterBlockContainsResidualBlock(values, r3);  }  void ExpectParameterBlockContains(double* values,                                    ResidualBlock* r1,                                    ResidualBlock* r2,                                    ResidualBlock* r3,                                    ResidualBlock* r4) {    ExpectSize(values, 4);    ExpectParameterBlockContainsResidualBlock(values, r1);    ExpectParameterBlockContainsResidualBlock(values, r2);    ExpectParameterBlockContainsResidualBlock(values, r3);    ExpectParameterBlockContainsResidualBlock(values, r4);  }  std::unique_ptr<ProblemImpl> problem;  double y[4], z[5], w[3];};TEST(Problem, SetParameterBlockConstantWithUnknownPtrDies) {  double x[3];  double y[2];  Problem problem;  problem.AddParameterBlock(x, 3);  EXPECT_DEATH_IF_SUPPORTED(problem.SetParameterBlockConstant(y),                            "Parameter block not found:");}TEST(Problem, SetParameterBlockVariableWithUnknownPtrDies) {  double x[3];  double y[2];  Problem problem;  problem.AddParameterBlock(x, 3);  EXPECT_DEATH_IF_SUPPORTED(problem.SetParameterBlockVariable(y),                            "Parameter block not found:");}TEST(Problem, IsParameterBlockConstant) {  double x1[3];  double x2[3];  Problem problem;  problem.AddParameterBlock(x1, 3);  problem.AddParameterBlock(x2, 3);  EXPECT_FALSE(problem.IsParameterBlockConstant(x1));  EXPECT_FALSE(problem.IsParameterBlockConstant(x2));  problem.SetParameterBlockConstant(x1);  EXPECT_TRUE(problem.IsParameterBlockConstant(x1));  EXPECT_FALSE(problem.IsParameterBlockConstant(x2));  problem.SetParameterBlockConstant(x2);  EXPECT_TRUE(problem.IsParameterBlockConstant(x1));  EXPECT_TRUE(problem.IsParameterBlockConstant(x2));  problem.SetParameterBlockVariable(x1);  EXPECT_FALSE(problem.IsParameterBlockConstant(x1));  EXPECT_TRUE(problem.IsParameterBlockConstant(x2));}TEST(Problem, IsParameterBlockConstantWithUnknownPtrDies) {  double x[3];  double y[2];  Problem problem;  problem.AddParameterBlock(x, 3);  EXPECT_DEATH_IF_SUPPORTED(problem.IsParameterBlockConstant(y),                            "Parameter block not found:");}TEST(Problem, SetLocalParameterizationWithUnknownPtrDies) {  double x[3];  double y[2];  Problem problem;  problem.AddParameterBlock(x, 3);  EXPECT_DEATH_IF_SUPPORTED(      problem.SetParameterization(y, new IdentityParameterization(3)),      "Parameter block not found:");}TEST(Problem, RemoveParameterBlockWithUnknownPtrDies) {  double x[3];  double y[2];  Problem problem;  problem.AddParameterBlock(x, 3);  EXPECT_DEATH_IF_SUPPORTED(      problem.RemoveParameterBlock(y), "Parameter block not found:");}TEST(Problem, GetParameterization) {  double x[3];  double y[2];  Problem problem;  problem.AddParameterBlock(x, 3);  problem.AddParameterBlock(y, 2);  LocalParameterization* parameterization =  new IdentityParameterization(3);  problem.SetParameterization(x, parameterization);  EXPECT_EQ(problem.GetParameterization(x), parameterization);  EXPECT_TRUE(problem.GetParameterization(y) == NULL);}TEST(Problem, ParameterBlockQueryTest) {  double x[3];  double y[4];  Problem problem;  problem.AddParameterBlock(x, 3);  problem.AddParameterBlock(y, 4);  vector<int> constant_parameters;  constant_parameters.push_back(0);  problem.SetParameterization(      x,      new SubsetParameterization(3, constant_parameters));  EXPECT_EQ(problem.ParameterBlockSize(x), 3);  EXPECT_EQ(problem.ParameterBlockLocalSize(x), 2);  EXPECT_EQ(problem.ParameterBlockLocalSize(y), 4);  vector<double*> parameter_blocks;  problem.GetParameterBlocks(¶meter_blocks);  EXPECT_EQ(parameter_blocks.size(), 2);  EXPECT_NE(parameter_blocks[0], parameter_blocks[1]);  EXPECT_TRUE(parameter_blocks[0] == x || parameter_blocks[0] == y);  EXPECT_TRUE(parameter_blocks[1] == x || parameter_blocks[1] == y);  EXPECT_TRUE(problem.HasParameterBlock(x));  problem.RemoveParameterBlock(x);  EXPECT_FALSE(problem.HasParameterBlock(x));  problem.GetParameterBlocks(¶meter_blocks);  EXPECT_EQ(parameter_blocks.size(), 1);  EXPECT_TRUE(parameter_blocks[0] == y);}TEST_P(DynamicProblem, RemoveParameterBlockWithNoResiduals) {  problem->AddParameterBlock(y, 4);  problem->AddParameterBlock(z, 5);  problem->AddParameterBlock(w, 3);  ASSERT_EQ(3, problem->NumParameterBlocks());  ASSERT_EQ(0, NumResidualBlocks());  EXPECT_EQ(y, GetParameterBlock(0)->user_state());  EXPECT_EQ(z, GetParameterBlock(1)->user_state());  EXPECT_EQ(w, GetParameterBlock(2)->user_state());  // w is at the end, which might break the swapping logic so try adding and  // removing it.  problem->RemoveParameterBlock(w);  ASSERT_EQ(2, problem->NumParameterBlocks());  ASSERT_EQ(0, NumResidualBlocks());  EXPECT_EQ(y, GetParameterBlock(0)->user_state());  EXPECT_EQ(z, GetParameterBlock(1)->user_state());  problem->AddParameterBlock(w, 3);  ASSERT_EQ(3, problem->NumParameterBlocks());  ASSERT_EQ(0, NumResidualBlocks());  EXPECT_EQ(y, GetParameterBlock(0)->user_state());  EXPECT_EQ(z, GetParameterBlock(1)->user_state());  EXPECT_EQ(w, GetParameterBlock(2)->user_state());  // Now remove z, which is in the middle, and add it back.  problem->RemoveParameterBlock(z);  ASSERT_EQ(2, problem->NumParameterBlocks());  ASSERT_EQ(0, NumResidualBlocks());  EXPECT_EQ(y, GetParameterBlock(0)->user_state());  EXPECT_EQ(w, GetParameterBlock(1)->user_state());  problem->AddParameterBlock(z, 5);  ASSERT_EQ(3, problem->NumParameterBlocks());  ASSERT_EQ(0, NumResidualBlocks());  EXPECT_EQ(y, GetParameterBlock(0)->user_state());  EXPECT_EQ(w, GetParameterBlock(1)->user_state());  EXPECT_EQ(z, GetParameterBlock(2)->user_state());  // Now remove everything.  // y  problem->RemoveParameterBlock(y);  ASSERT_EQ(2, problem->NumParameterBlocks());  ASSERT_EQ(0, NumResidualBlocks());  EXPECT_EQ(z, GetParameterBlock(0)->user_state());  EXPECT_EQ(w, GetParameterBlock(1)->user_state());  // z  problem->RemoveParameterBlock(z);  ASSERT_EQ(1, problem->NumParameterBlocks());  ASSERT_EQ(0, NumResidualBlocks());  EXPECT_EQ(w, GetParameterBlock(0)->user_state());  // w  problem->RemoveParameterBlock(w);  EXPECT_EQ(0, problem->NumParameterBlocks());  EXPECT_EQ(0, NumResidualBlocks());}TEST_P(DynamicProblem, RemoveParameterBlockWithResiduals) {  problem->AddParameterBlock(y, 4);  problem->AddParameterBlock(z, 5);  problem->AddParameterBlock(w, 3);  ASSERT_EQ(3, problem->NumParameterBlocks());  ASSERT_EQ(0, NumResidualBlocks());  EXPECT_EQ(y, GetParameterBlock(0)->user_state());  EXPECT_EQ(z, GetParameterBlock(1)->user_state());  EXPECT_EQ(w, GetParameterBlock(2)->user_state());  // Add all combinations of cost functions.  CostFunction* cost_yzw = new TernaryCostFunction(1, 4, 5, 3);  CostFunction* cost_yz  = new BinaryCostFunction (1, 4, 5);  CostFunction* cost_yw  = new BinaryCostFunction (1, 4, 3);  CostFunction* cost_zw  = new BinaryCostFunction (1, 5, 3);  CostFunction* cost_y   = new UnaryCostFunction  (1, 4);  CostFunction* cost_z   = new UnaryCostFunction  (1, 5);  CostFunction* cost_w   = new UnaryCostFunction  (1, 3);  ResidualBlock* r_yzw = problem->AddResidualBlock(cost_yzw, NULL, y, z, w);  ResidualBlock* r_yz  = problem->AddResidualBlock(cost_yz,  NULL, y, z);  ResidualBlock* r_yw  = problem->AddResidualBlock(cost_yw,  NULL, y, w);  ResidualBlock* r_zw  = problem->AddResidualBlock(cost_zw,  NULL, z, w);  ResidualBlock* r_y   = problem->AddResidualBlock(cost_y,   NULL, y);  ResidualBlock* r_z   = problem->AddResidualBlock(cost_z,   NULL, z);  ResidualBlock* r_w   = problem->AddResidualBlock(cost_w,   NULL, w);  EXPECT_EQ(3, problem->NumParameterBlocks());  EXPECT_EQ(7, NumResidualBlocks());  // Remove w, which should remove r_yzw, r_yw, r_zw, r_w.  problem->RemoveParameterBlock(w);  ASSERT_EQ(2, problem->NumParameterBlocks());  ASSERT_EQ(3, NumResidualBlocks());  ASSERT_FALSE(HasResidualBlock(r_yzw));  ASSERT_TRUE (HasResidualBlock(r_yz ));  ASSERT_FALSE(HasResidualBlock(r_yw ));  ASSERT_FALSE(HasResidualBlock(r_zw ));  ASSERT_TRUE (HasResidualBlock(r_y  ));  ASSERT_TRUE (HasResidualBlock(r_z  ));  ASSERT_FALSE(HasResidualBlock(r_w  ));  // Remove z, which will remove almost everything else.  problem->RemoveParameterBlock(z);  ASSERT_EQ(1, problem->NumParameterBlocks());  ASSERT_EQ(1, NumResidualBlocks());  ASSERT_FALSE(HasResidualBlock(r_yzw));  ASSERT_FALSE(HasResidualBlock(r_yz ));  ASSERT_FALSE(HasResidualBlock(r_yw ));  ASSERT_FALSE(HasResidualBlock(r_zw ));  ASSERT_TRUE (HasResidualBlock(r_y  ));  ASSERT_FALSE(HasResidualBlock(r_z  ));  ASSERT_FALSE(HasResidualBlock(r_w  ));  // Remove y; all gone.  problem->RemoveParameterBlock(y);  EXPECT_EQ(0, problem->NumParameterBlocks());  EXPECT_EQ(0, NumResidualBlocks());}TEST_P(DynamicProblem, RemoveResidualBlock) {  problem->AddParameterBlock(y, 4);  problem->AddParameterBlock(z, 5);  problem->AddParameterBlock(w, 3);  // Add all combinations of cost functions.  CostFunction* cost_yzw = new TernaryCostFunction(1, 4, 5, 3);  CostFunction* cost_yz  = new BinaryCostFunction (1, 4, 5);  CostFunction* cost_yw  = new BinaryCostFunction (1, 4, 3);  CostFunction* cost_zw  = new BinaryCostFunction (1, 5, 3);  CostFunction* cost_y   = new UnaryCostFunction  (1, 4);  CostFunction* cost_z   = new UnaryCostFunction  (1, 5);  CostFunction* cost_w   = new UnaryCostFunction  (1, 3);  ResidualBlock* r_yzw = problem->AddResidualBlock(cost_yzw, NULL, y, z, w);  ResidualBlock* r_yz  = problem->AddResidualBlock(cost_yz,  NULL, y, z);  ResidualBlock* r_yw  = problem->AddResidualBlock(cost_yw,  NULL, y, w);  ResidualBlock* r_zw  = problem->AddResidualBlock(cost_zw,  NULL, z, w);  ResidualBlock* r_y   = problem->AddResidualBlock(cost_y,   NULL, y);  ResidualBlock* r_z   = problem->AddResidualBlock(cost_z,   NULL, z);  ResidualBlock* r_w   = problem->AddResidualBlock(cost_w,   NULL, w);  if (GetParam()) {    // In this test parameterization, there should be back-pointers from the    // parameter blocks to the residual blocks.    ExpectParameterBlockContains(y, r_yzw, r_yz, r_yw, r_y);    ExpectParameterBlockContains(z, r_yzw, r_yz, r_zw, r_z);    ExpectParameterBlockContains(w, r_yzw, r_yw, r_zw, r_w);  } else {    // Otherwise, nothing.    EXPECT_TRUE(GetParameterBlock(0)->mutable_residual_blocks() == NULL);    EXPECT_TRUE(GetParameterBlock(1)->mutable_residual_blocks() == NULL);    EXPECT_TRUE(GetParameterBlock(2)->mutable_residual_blocks() == NULL);  }  EXPECT_EQ(3, problem->NumParameterBlocks());  EXPECT_EQ(7, NumResidualBlocks());  // Remove each residual and check the state after each removal.  // Remove r_yzw.  problem->RemoveResidualBlock(r_yzw);  ASSERT_EQ(3, problem->NumParameterBlocks());  ASSERT_EQ(6, NumResidualBlocks());  if (GetParam()) {    ExpectParameterBlockContains(y, r_yz, r_yw, r_y);    ExpectParameterBlockContains(z, r_yz, r_zw, r_z);    ExpectParameterBlockContains(w, r_yw, r_zw, r_w);  }  ASSERT_TRUE (HasResidualBlock(r_yz ));  ASSERT_TRUE (HasResidualBlock(r_yw ));  ASSERT_TRUE (HasResidualBlock(r_zw ));  ASSERT_TRUE (HasResidualBlock(r_y  ));  ASSERT_TRUE (HasResidualBlock(r_z  ));  ASSERT_TRUE (HasResidualBlock(r_w  ));  // Remove r_yw.  problem->RemoveResidualBlock(r_yw);  ASSERT_EQ(3, problem->NumParameterBlocks());  ASSERT_EQ(5, NumResidualBlocks());  if (GetParam()) {    ExpectParameterBlockContains(y, r_yz, r_y);    ExpectParameterBlockContains(z, r_yz, r_zw, r_z);    ExpectParameterBlockContains(w, r_zw, r_w);  }  ASSERT_TRUE (HasResidualBlock(r_yz ));  ASSERT_TRUE (HasResidualBlock(r_zw ));  ASSERT_TRUE (HasResidualBlock(r_y  ));  ASSERT_TRUE (HasResidualBlock(r_z  ));  ASSERT_TRUE (HasResidualBlock(r_w  ));  // Remove r_zw.  problem->RemoveResidualBlock(r_zw);  ASSERT_EQ(3, problem->NumParameterBlocks());  ASSERT_EQ(4, NumResidualBlocks());  if (GetParam()) {    ExpectParameterBlockContains(y, r_yz, r_y);    ExpectParameterBlockContains(z, r_yz, r_z);    ExpectParameterBlockContains(w, r_w);  }  ASSERT_TRUE (HasResidualBlock(r_yz ));  ASSERT_TRUE (HasResidualBlock(r_y  ));  ASSERT_TRUE (HasResidualBlock(r_z  ));  ASSERT_TRUE (HasResidualBlock(r_w  ));  // Remove r_w.  problem->RemoveResidualBlock(r_w);  ASSERT_EQ(3, problem->NumParameterBlocks());  ASSERT_EQ(3, NumResidualBlocks());  if (GetParam()) {    ExpectParameterBlockContains(y, r_yz, r_y);    ExpectParameterBlockContains(z, r_yz, r_z);    ExpectParameterBlockContains(w);  }  ASSERT_TRUE (HasResidualBlock(r_yz ));  ASSERT_TRUE (HasResidualBlock(r_y  ));  ASSERT_TRUE (HasResidualBlock(r_z  ));  // Remove r_yz.  problem->RemoveResidualBlock(r_yz);  ASSERT_EQ(3, problem->NumParameterBlocks());  ASSERT_EQ(2, NumResidualBlocks());  if (GetParam()) {    ExpectParameterBlockContains(y, r_y);    ExpectParameterBlockContains(z, r_z);    ExpectParameterBlockContains(w);  }  ASSERT_TRUE (HasResidualBlock(r_y  ));  ASSERT_TRUE (HasResidualBlock(r_z  ));  // Remove the last two.  problem->RemoveResidualBlock(r_z);  problem->RemoveResidualBlock(r_y);  ASSERT_EQ(3, problem->NumParameterBlocks());  ASSERT_EQ(0, NumResidualBlocks());  if (GetParam()) {    ExpectParameterBlockContains(y);    ExpectParameterBlockContains(z);    ExpectParameterBlockContains(w);  }}TEST_P(DynamicProblem, RemoveInvalidResidualBlockDies) {  problem->AddParameterBlock(y, 4);  problem->AddParameterBlock(z, 5);  problem->AddParameterBlock(w, 3);  // Add all combinations of cost functions.  CostFunction* cost_yzw = new TernaryCostFunction(1, 4, 5, 3);  CostFunction* cost_yz  = new BinaryCostFunction (1, 4, 5);  CostFunction* cost_yw  = new BinaryCostFunction (1, 4, 3);  CostFunction* cost_zw  = new BinaryCostFunction (1, 5, 3);  CostFunction* cost_y   = new UnaryCostFunction  (1, 4);  CostFunction* cost_z   = new UnaryCostFunction  (1, 5);  CostFunction* cost_w   = new UnaryCostFunction  (1, 3);  ResidualBlock* r_yzw = problem->AddResidualBlock(cost_yzw, NULL, y, z, w);  ResidualBlock* r_yz  = problem->AddResidualBlock(cost_yz,  NULL, y, z);  ResidualBlock* r_yw  = problem->AddResidualBlock(cost_yw,  NULL, y, w);  ResidualBlock* r_zw  = problem->AddResidualBlock(cost_zw,  NULL, z, w);  ResidualBlock* r_y   = problem->AddResidualBlock(cost_y,   NULL, y);  ResidualBlock* r_z   = problem->AddResidualBlock(cost_z,   NULL, z);  ResidualBlock* r_w   = problem->AddResidualBlock(cost_w,   NULL, w);  // Remove r_yzw.  problem->RemoveResidualBlock(r_yzw);  ASSERT_EQ(3, problem->NumParameterBlocks());  ASSERT_EQ(6, NumResidualBlocks());  // Attempt to remove r_yzw again.  EXPECT_DEATH_IF_SUPPORTED(problem->RemoveResidualBlock(r_yzw), "not found");  // Attempt to remove a cast pointer never added as a residual.  int trash_memory = 1234;  ResidualBlock* invalid_residual =      reinterpret_cast<ResidualBlock*>(&trash_memory);  EXPECT_DEATH_IF_SUPPORTED(problem->RemoveResidualBlock(invalid_residual),                            "not found");  // Remove a parameter block, which in turn removes the dependent residuals  // then attempt to remove them directly.  problem->RemoveParameterBlock(z);  ASSERT_EQ(2, problem->NumParameterBlocks());  ASSERT_EQ(3, NumResidualBlocks());  EXPECT_DEATH_IF_SUPPORTED(problem->RemoveResidualBlock(r_yz), "not found");  EXPECT_DEATH_IF_SUPPORTED(problem->RemoveResidualBlock(r_zw), "not found");  EXPECT_DEATH_IF_SUPPORTED(problem->RemoveResidualBlock(r_z), "not found");  problem->RemoveResidualBlock(r_yw);  problem->RemoveResidualBlock(r_w);  problem->RemoveResidualBlock(r_y);}// Check that a null-terminated array, a, has the same elements as b.template<typename T>void ExpectVectorContainsUnordered(const T* a, const vector<T>& b) {  // Compute the size of a.  int size = 0;  while (a[size]) {    ++size;  }  ASSERT_EQ(size, b.size());  // Sort a.  vector<T> a_sorted(size);  copy(a, a + size, a_sorted.begin());  sort(a_sorted.begin(), a_sorted.end());  // Sort b.  vector<T> b_sorted(b);  sort(b_sorted.begin(), b_sorted.end());  // Compare.  for (int i = 0; i < size; ++i) {    EXPECT_EQ(a_sorted[i], b_sorted[i]);  }}void ExpectProblemHasResidualBlocks(    const ProblemImpl &problem,    const ResidualBlockId *expected_residual_blocks) {  vector<ResidualBlockId> residual_blocks;  problem.GetResidualBlocks(&residual_blocks);  ExpectVectorContainsUnordered(expected_residual_blocks, residual_blocks);}TEST_P(DynamicProblem, GetXXXBlocksForYYYBlock) {  problem->AddParameterBlock(y, 4);  problem->AddParameterBlock(z, 5);  problem->AddParameterBlock(w, 3);  // Add all combinations of cost functions.  CostFunction* cost_yzw = new TernaryCostFunction(1, 4, 5, 3);  CostFunction* cost_yz  = new BinaryCostFunction (1, 4, 5);  CostFunction* cost_yw  = new BinaryCostFunction (1, 4, 3);  CostFunction* cost_zw  = new BinaryCostFunction (1, 5, 3);  CostFunction* cost_y   = new UnaryCostFunction  (1, 4);  CostFunction* cost_z   = new UnaryCostFunction  (1, 5);  CostFunction* cost_w   = new UnaryCostFunction  (1, 3);  ResidualBlock* r_yzw = problem->AddResidualBlock(cost_yzw, NULL, y, z, w);  {    ResidualBlockId expected_residuals[] = {r_yzw, 0};    ExpectProblemHasResidualBlocks(*problem, expected_residuals);  }  ResidualBlock* r_yz  = problem->AddResidualBlock(cost_yz,  NULL, y, z);  {    ResidualBlockId expected_residuals[] = {r_yzw, r_yz, 0};    ExpectProblemHasResidualBlocks(*problem, expected_residuals);  }  ResidualBlock* r_yw  = problem->AddResidualBlock(cost_yw,  NULL, y, w);  {    ResidualBlock *expected_residuals[] = {r_yzw, r_yz, r_yw, 0};    ExpectProblemHasResidualBlocks(*problem, expected_residuals);  }  ResidualBlock* r_zw  = problem->AddResidualBlock(cost_zw,  NULL, z, w);  {    ResidualBlock *expected_residuals[] = {r_yzw, r_yz, r_yw, r_zw, 0};    ExpectProblemHasResidualBlocks(*problem, expected_residuals);  }  ResidualBlock* r_y   = problem->AddResidualBlock(cost_y,   NULL, y);  {    ResidualBlock *expected_residuals[] = {r_yzw, r_yz, r_yw, r_zw, r_y, 0};    ExpectProblemHasResidualBlocks(*problem, expected_residuals);  }  ResidualBlock* r_z   = problem->AddResidualBlock(cost_z,   NULL, z);  {    ResidualBlock *expected_residuals[] = {      r_yzw, r_yz, r_yw, r_zw, r_y, r_z, 0    };    ExpectProblemHasResidualBlocks(*problem, expected_residuals);  }  ResidualBlock* r_w   = problem->AddResidualBlock(cost_w,   NULL, w);  {    ResidualBlock *expected_residuals[] = {      r_yzw, r_yz, r_yw, r_zw, r_y, r_z, r_w, 0    };    ExpectProblemHasResidualBlocks(*problem, expected_residuals);  }  vector<double*> parameter_blocks;  vector<ResidualBlockId> residual_blocks;  // Check GetResidualBlocksForParameterBlock() for all parameter blocks.  struct GetResidualBlocksForParameterBlockTestCase {    double* parameter_block;    ResidualBlockId expected_residual_blocks[10];  };  GetResidualBlocksForParameterBlockTestCase get_residual_blocks_cases[] = {    { y, { r_yzw, r_yz, r_yw, r_y, NULL} },    { z, { r_yzw, r_yz, r_zw, r_z, NULL} },    { w, { r_yzw, r_yw, r_zw, r_w, NULL} },    { NULL }  };  for (int i = 0; get_residual_blocks_cases[i].parameter_block; ++i) {    problem->GetResidualBlocksForParameterBlock(        get_residual_blocks_cases[i].parameter_block,        &residual_blocks);    ExpectVectorContainsUnordered(        get_residual_blocks_cases[i].expected_residual_blocks,        residual_blocks);  }  // Check GetParameterBlocksForResidualBlock() for all residual blocks.  struct GetParameterBlocksForResidualBlockTestCase {    ResidualBlockId residual_block;    double* expected_parameter_blocks[10];  };  GetParameterBlocksForResidualBlockTestCase get_parameter_blocks_cases[] = {    { r_yzw, { y, z, w, NULL } },    { r_yz , { y, z, NULL } },    { r_yw , { y, w, NULL } },    { r_zw , { z, w, NULL } },    { r_y  , { y, NULL } },    { r_z  , { z, NULL } },    { r_w  , { w, NULL } },    { NULL }  };  for (int i = 0; get_parameter_blocks_cases[i].residual_block; ++i) {    problem->GetParameterBlocksForResidualBlock(        get_parameter_blocks_cases[i].residual_block,        ¶meter_blocks);    ExpectVectorContainsUnordered(        get_parameter_blocks_cases[i].expected_parameter_blocks,        parameter_blocks);  }}INSTANTIATE_TEST_CASE_P(OptionsInstantiation,                        DynamicProblem,                        ::testing::Values(true, false));// Test for Problem::Evaluate// r_i = i - (j + 1) * x_ij^2template <int kNumResiduals, int kNumParameterBlocks>class QuadraticCostFunction : public CostFunction { public:  QuadraticCostFunction() {    CHECK_GT(kNumResiduals, 0);    CHECK_GT(kNumParameterBlocks, 0);    set_num_residuals(kNumResiduals);    for (int i = 0; i < kNumParameterBlocks; ++i) {      mutable_parameter_block_sizes()->push_back(kNumResiduals);    }  }  virtual bool Evaluate(double const* const* parameters,                        double* residuals,                        double** jacobians) const {    for (int i = 0; i < kNumResiduals; ++i) {      residuals[i] = i;      for (int j = 0; j < kNumParameterBlocks; ++j) {        residuals[i] -= (j + 1.0) * parameters[j][i] * parameters[j][i];      }    }    if (jacobians == NULL) {      return true;    }    for (int j = 0; j < kNumParameterBlocks; ++j) {      if (jacobians[j] != NULL) {        MatrixRef(jacobians[j], kNumResiduals, kNumResiduals) =            (-2.0 * (j + 1.0) *             ConstVectorRef(parameters[j], kNumResiduals)).asDiagonal();      }    }    return true;  }};// Convert a CRSMatrix to a dense Eigen matrix.void CRSToDenseMatrix(const CRSMatrix& input, Matrix* output) {  Matrix& m = *CHECK_NOTNULL(output);  m.resize(input.num_rows, input.num_cols);  m.setZero();  for (int row = 0; row < input.num_rows; ++row) {    for (int j = input.rows[row]; j < input.rows[row + 1]; ++j) {      const int col = input.cols[j];      m(row, col) = input.values[j];    }  }}class ProblemEvaluateTest : public ::testing::Test { protected:  void SetUp() {    for (int i = 0; i < 6; ++i) {      parameters_[i] = static_cast<double>(i + 1);    }    parameter_blocks_.push_back(parameters_);    parameter_blocks_.push_back(parameters_ + 2);    parameter_blocks_.push_back(parameters_ + 4);    CostFunction* cost_function = new QuadraticCostFunction<2, 2>;    // f(x, y)    residual_blocks_.push_back(        problem_.AddResidualBlock(cost_function,                                  NULL,                                  parameters_,                                  parameters_ + 2));    // g(y, z)    residual_blocks_.push_back(        problem_.AddResidualBlock(cost_function,                                  NULL, parameters_ + 2,                                  parameters_ + 4));    // h(z, x)    residual_blocks_.push_back(        problem_.AddResidualBlock(cost_function,                                  NULL,                                  parameters_ + 4,                                  parameters_));  }  void TearDown() {    EXPECT_TRUE(problem_.program().IsValid());  }  void 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) {    double cost;    vector<double> residuals;    vector<double> gradient;    CRSMatrix jacobian;    EXPECT_TRUE(        problem_.Evaluate(options,                          &cost,                          expected_residuals != NULL ? &residuals : NULL,                          expected_gradient != NULL ? &gradient : NULL,                          expected_jacobian != NULL ? &jacobian : NULL));    if (expected_residuals != NULL) {      EXPECT_EQ(residuals.size(), expected_num_rows);    }    if (expected_gradient != NULL) {      EXPECT_EQ(gradient.size(), expected_num_cols);    }    if (expected_jacobian != NULL) {      EXPECT_EQ(jacobian.num_rows, expected_num_rows);      EXPECT_EQ(jacobian.num_cols, expected_num_cols);    }    Matrix dense_jacobian;    if (expected_jacobian != NULL) {      CRSToDenseMatrix(jacobian, &dense_jacobian);    }    CompareEvaluations(expected_num_rows,                       expected_num_cols,                       expected_cost,                       expected_residuals,                       expected_gradient,                       expected_jacobian,                       cost,                       residuals.size() > 0 ? &residuals[0] : NULL,                       gradient.size() > 0 ? &gradient[0] : NULL,                       dense_jacobian.data());  }  void CheckAllEvaluationCombinations(const Problem::EvaluateOptions& options,                                      const ExpectedEvaluation& expected) {    for (int i = 0; i < 8; ++i) {      EvaluateAndCompare(options,                         expected.num_rows,                         expected.num_cols,                         expected.cost,                         (i & 1) ? expected.residuals : NULL,                         (i & 2) ? expected.gradient  : NULL,                         (i & 4) ? expected.jacobian  : NULL);    }  }  ProblemImpl problem_;  double parameters_[6];  vector<double*> parameter_blocks_;  vector<ResidualBlockId> residual_blocks_;};TEST_F(ProblemEvaluateTest, MultipleParameterAndResidualBlocks) {  ExpectedEvaluation expected = {    // Rows/columns    6, 6,    // Cost    7607.0,    // Residuals    { -19.0, -35.0,  // f      -59.0, -87.0,  // g      -27.0, -43.0   // h    },    // Gradient    {  146.0,  484.0,   // x       582.0, 1256.0,   // y      1450.0, 2604.0,   // z    },    // Jacobian    //                       x             y             z    { /* f(x, y) */ -2.0,  0.0, -12.0,   0.0,   0.0,   0.0,                     0.0, -4.0,   0.0, -16.0,   0.0,   0.0,      /* g(y, z) */  0.0,  0.0,  -6.0,   0.0, -20.0,   0.0,                     0.0,  0.0,   0.0,  -8.0,   0.0, -24.0,      /* h(z, x) */ -4.0,  0.0,   0.0,   0.0, -10.0,   0.0,                     0.0, -8.0,   0.0,   0.0,   0.0, -12.0    }  };  CheckAllEvaluationCombinations(Problem::EvaluateOptions(), expected);}TEST_F(ProblemEvaluateTest, ParameterAndResidualBlocksPassedInOptions) {  ExpectedEvaluation expected = {    // Rows/columns    6, 6,    // Cost    7607.0,    // Residuals    { -19.0, -35.0,  // f      -59.0, -87.0,  // g      -27.0, -43.0   // h    },    // Gradient    {  146.0,  484.0,   // x       582.0, 1256.0,   // y      1450.0, 2604.0,   // z    },    // Jacobian    //                       x             y             z    { /* f(x, y) */ -2.0,  0.0, -12.0,   0.0,   0.0,   0.0,                     0.0, -4.0,   0.0, -16.0,   0.0,   0.0,      /* g(y, z) */  0.0,  0.0,  -6.0,   0.0, -20.0,   0.0,                     0.0,  0.0,   0.0,  -8.0,   0.0, -24.0,      /* h(z, x) */ -4.0,  0.0,   0.0,   0.0, -10.0,   0.0,                     0.0, -8.0,   0.0,   0.0,   0.0, -12.0    }  };  Problem::EvaluateOptions evaluate_options;  evaluate_options.parameter_blocks = parameter_blocks_;  evaluate_options.residual_blocks = residual_blocks_;  CheckAllEvaluationCombinations(evaluate_options, expected);}TEST_F(ProblemEvaluateTest, ReorderedResidualBlocks) {  ExpectedEvaluation expected = {    // Rows/columns    6, 6,    // Cost    7607.0,    // Residuals    { -19.0, -35.0,  // f      -27.0, -43.0,  // h      -59.0, -87.0   // g    },    // Gradient    {  146.0,  484.0,   // x       582.0, 1256.0,   // y      1450.0, 2604.0,   // z    },    // Jacobian    //                       x             y             z    { /* f(x, y) */ -2.0,  0.0, -12.0,   0.0,   0.0,   0.0,                     0.0, -4.0,   0.0, -16.0,   0.0,   0.0,      /* h(z, x) */ -4.0,  0.0,   0.0,   0.0, -10.0,   0.0,                     0.0, -8.0,   0.0,   0.0,   0.0, -12.0,      /* g(y, z) */  0.0,  0.0,  -6.0,   0.0, -20.0,   0.0,                     0.0,  0.0,   0.0,  -8.0,   0.0, -24.0    }  };  Problem::EvaluateOptions evaluate_options;  evaluate_options.parameter_blocks = parameter_blocks_;  // f, h, g  evaluate_options.residual_blocks.push_back(residual_blocks_[0]);  evaluate_options.residual_blocks.push_back(residual_blocks_[2]);  evaluate_options.residual_blocks.push_back(residual_blocks_[1]);  CheckAllEvaluationCombinations(evaluate_options, expected);}TEST_F(ProblemEvaluateTest, ReorderedResidualBlocksAndReorderedParameterBlocks) {  ExpectedEvaluation expected = {    // Rows/columns    6, 6,    // Cost    7607.0,    // Residuals    { -19.0, -35.0,  // f      -27.0, -43.0,  // h      -59.0, -87.0   // g    },    // Gradient    {  1450.0, 2604.0,   // z        582.0, 1256.0,   // y        146.0,  484.0,   // x    },     // Jacobian    //                       z             y             x    { /* f(x, y) */   0.0,   0.0, -12.0,   0.0,  -2.0,   0.0,                      0.0,   0.0,   0.0, -16.0,   0.0,  -4.0,      /* h(z, x) */ -10.0,   0.0,   0.0,   0.0,  -4.0,   0.0,                      0.0, -12.0,   0.0,   0.0,   0.0,  -8.0,      /* g(y, z) */ -20.0,   0.0,  -6.0,   0.0,   0.0,   0.0,                      0.0, -24.0,   0.0,  -8.0,   0.0,   0.0    }  };  Problem::EvaluateOptions evaluate_options;  // z, y, x  evaluate_options.parameter_blocks.push_back(parameter_blocks_[2]);  evaluate_options.parameter_blocks.push_back(parameter_blocks_[1]);  evaluate_options.parameter_blocks.push_back(parameter_blocks_[0]);  // f, h, g  evaluate_options.residual_blocks.push_back(residual_blocks_[0]);  evaluate_options.residual_blocks.push_back(residual_blocks_[2]);  evaluate_options.residual_blocks.push_back(residual_blocks_[1]);  CheckAllEvaluationCombinations(evaluate_options, expected);}TEST_F(ProblemEvaluateTest, ConstantParameterBlock) {  ExpectedEvaluation expected = {    // Rows/columns    6, 6,    // Cost    7607.0,    // Residuals    { -19.0, -35.0,  // f      -59.0, -87.0,  // g      -27.0, -43.0   // h    },    // Gradient    {  146.0,  484.0,  // x         0.0,    0.0,  // y      1450.0, 2604.0,  // z    },    // Jacobian    //                       x             y             z    { /* f(x, y) */ -2.0,  0.0,   0.0,   0.0,   0.0,   0.0,                     0.0, -4.0,   0.0,   0.0,   0.0,   0.0,      /* g(y, z) */  0.0,  0.0,   0.0,   0.0, -20.0,   0.0,                     0.0,  0.0,   0.0,   0.0,   0.0, -24.0,      /* h(z, x) */ -4.0,  0.0,   0.0,   0.0, -10.0,   0.0,                     0.0, -8.0,   0.0,   0.0,   0.0, -12.0    }  };  problem_.SetParameterBlockConstant(parameters_ + 2);  CheckAllEvaluationCombinations(Problem::EvaluateOptions(), expected);}TEST_F(ProblemEvaluateTest, ExcludedAResidualBlock) {  ExpectedEvaluation expected = {    // Rows/columns    4, 6,    // Cost    2082.0,    // Residuals    { -19.0, -35.0,  // f      -27.0, -43.0   // h    },    // Gradient    {  146.0,  484.0,   // x       228.0,  560.0,   // y       270.0,  516.0,   // z    },    // Jacobian    //                       x             y             z    { /* f(x, y) */ -2.0,  0.0, -12.0,   0.0,   0.0,   0.0,                     0.0, -4.0,   0.0, -16.0,   0.0,   0.0,      /* h(z, x) */ -4.0,  0.0,   0.0,   0.0, -10.0,   0.0,                     0.0, -8.0,   0.0,   0.0,   0.0, -12.0    }  };  Problem::EvaluateOptions evaluate_options;  evaluate_options.residual_blocks.push_back(residual_blocks_[0]);  evaluate_options.residual_blocks.push_back(residual_blocks_[2]);  CheckAllEvaluationCombinations(evaluate_options, expected);}TEST_F(ProblemEvaluateTest, ExcludedParameterBlock) {  ExpectedEvaluation expected = {    // Rows/columns    6, 4,    // Cost    7607.0,    // Residuals    { -19.0, -35.0,  // f      -59.0, -87.0,  // g      -27.0, -43.0   // h    },    // Gradient    {  146.0,  484.0,  // x      1450.0, 2604.0,  // z    },    // Jacobian    //                       x             z    { /* f(x, y) */ -2.0,  0.0,   0.0,   0.0,                     0.0, -4.0,   0.0,   0.0,      /* g(y, z) */  0.0,  0.0, -20.0,   0.0,                     0.0,  0.0,   0.0, -24.0,      /* h(z, x) */ -4.0,  0.0, -10.0,   0.0,                     0.0, -8.0,   0.0, -12.0    }  };  Problem::EvaluateOptions evaluate_options;  // x, z  evaluate_options.parameter_blocks.push_back(parameter_blocks_[0]);  evaluate_options.parameter_blocks.push_back(parameter_blocks_[2]);  evaluate_options.residual_blocks = residual_blocks_;  CheckAllEvaluationCombinations(evaluate_options, expected);}TEST_F(ProblemEvaluateTest, ExcludedParameterBlockAndExcludedResidualBlock) {  ExpectedEvaluation expected = {    // Rows/columns    4, 4,    // Cost    6318.0,    // Residuals    { -19.0, -35.0,  // f      -59.0, -87.0,  // g    },    // Gradient    {   38.0,  140.0,  // x      1180.0, 2088.0,  // z    },    // Jacobian    //                       x             z    { /* f(x, y) */ -2.0,  0.0,   0.0,   0.0,                     0.0, -4.0,   0.0,   0.0,      /* g(y, z) */  0.0,  0.0, -20.0,   0.0,                     0.0,  0.0,   0.0, -24.0,    }  };  Problem::EvaluateOptions evaluate_options;  // x, z  evaluate_options.parameter_blocks.push_back(parameter_blocks_[0]);  evaluate_options.parameter_blocks.push_back(parameter_blocks_[2]);  evaluate_options.residual_blocks.push_back(residual_blocks_[0]);  evaluate_options.residual_blocks.push_back(residual_blocks_[1]);  CheckAllEvaluationCombinations(evaluate_options, expected);}TEST_F(ProblemEvaluateTest, LocalParameterization) {  ExpectedEvaluation expected = {    // Rows/columns    6, 5,    // Cost    7607.0,    // Residuals    { -19.0, -35.0,  // f      -59.0, -87.0,  // g      -27.0, -43.0   // h    },    // Gradient    {  146.0,  484.0,  // x      1256.0,          // y with SubsetParameterization      1450.0, 2604.0,  // z    },    // Jacobian    //                       x      y             z    { /* f(x, y) */ -2.0,  0.0,   0.0,   0.0,   0.0,                     0.0, -4.0, -16.0,   0.0,   0.0,      /* g(y, z) */  0.0,  0.0,   0.0, -20.0,   0.0,                     0.0,  0.0,  -8.0,   0.0, -24.0,      /* h(z, x) */ -4.0,  0.0,   0.0, -10.0,   0.0,                     0.0, -8.0,   0.0,   0.0, -12.0    }  };  vector<int> constant_parameters;  constant_parameters.push_back(0);  problem_.SetParameterization(parameters_ + 2,                               new SubsetParameterization(2,                                                          constant_parameters));  CheckAllEvaluationCombinations(Problem::EvaluateOptions(), expected);}TEST(Problem, SetAndGetParameterLowerBound) {  Problem problem;  double x[] = {1.0, 2.0};  problem.AddParameterBlock(x, 2);  EXPECT_EQ(problem.GetParameterLowerBound(x, 0),            -std::numeric_limits<double>::max());  EXPECT_EQ(problem.GetParameterLowerBound(x, 1),            -std::numeric_limits<double>::max());  problem.SetParameterLowerBound(x, 0, -1.0);  EXPECT_EQ(problem.GetParameterLowerBound(x, 0), -1.0);  EXPECT_EQ(problem.GetParameterLowerBound(x, 1),            -std::numeric_limits<double>::max());  problem.SetParameterLowerBound(x, 0, -2.0);  EXPECT_EQ(problem.GetParameterLowerBound(x, 0), -2.0);  EXPECT_EQ(problem.GetParameterLowerBound(x, 1),            -std::numeric_limits<double>::max());  problem.SetParameterLowerBound(x, 0, -std::numeric_limits<double>::max());  EXPECT_EQ(problem.GetParameterLowerBound(x, 0),            -std::numeric_limits<double>::max());  EXPECT_EQ(problem.GetParameterLowerBound(x, 1),            -std::numeric_limits<double>::max());}TEST(Problem, SetAndGetParameterUpperBound) {  Problem problem;  double x[] = {1.0, 2.0};  problem.AddParameterBlock(x, 2);  EXPECT_EQ(problem.GetParameterUpperBound(x, 0),            std::numeric_limits<double>::max());  EXPECT_EQ(problem.GetParameterUpperBound(x, 1),            std::numeric_limits<double>::max());  problem.SetParameterUpperBound(x, 0, -1.0);  EXPECT_EQ(problem.GetParameterUpperBound(x, 0), -1.0);  EXPECT_EQ(problem.GetParameterUpperBound(x, 1),            std::numeric_limits<double>::max());  problem.SetParameterUpperBound(x, 0, -2.0);  EXPECT_EQ(problem.GetParameterUpperBound(x, 0), -2.0);  EXPECT_EQ(problem.GetParameterUpperBound(x, 1),            std::numeric_limits<double>::max());  problem.SetParameterUpperBound(x, 0, std::numeric_limits<double>::max());  EXPECT_EQ(problem.GetParameterUpperBound(x, 0),            std::numeric_limits<double>::max());  EXPECT_EQ(problem.GetParameterUpperBound(x, 1),            std::numeric_limits<double>::max());}}  // namespace internal}  // namespace ceres
 |