| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250 | // Ceres Solver - A fast non-linear least squares minimizer// Copyright 2010, 2011, 2012 Google Inc. All rights reserved.// http://code.google.com/p/ceres-solver///// 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 "ceres/casts.h"#include "ceres/cost_function.h"#include "ceres/crs_matrix.h"#include "ceres/evaluator_test_utils.cc"#include "ceres/internal/eigen.h"#include "ceres/internal/scoped_ptr.h"#include "ceres/local_parameterization.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 {// 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, int16 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,                     int16 parameter_block1_size,                     int16 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,                      int16 parameter_block1_size,                      int16 parameter_block2_size,                      int16 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, 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());    // In the current implementation, the destructor shouldn't get run yet.    problem.RemoveResidualBlock(r_yz);    CHECK_EQ(num_destructions, 0);    problem.RemoveResidualBlock(r_wz);    CHECK_EQ(num_destructions, 0);    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_parameter_block_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) {    return find(problem->program().residual_blocks().begin(),                problem->program().residual_blocks().end(),                residual_block) != problem->program().residual_blocks().end();  }  // 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);  }  scoped_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, 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, 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);  problem.RemoveParameterBlock(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, problem->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, problem->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, problem->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, problem->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, problem->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, problem->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, problem->NumResidualBlocks());  EXPECT_EQ(w, GetParameterBlock(0)->user_state());  // w  problem->RemoveParameterBlock(w);  EXPECT_EQ(0, problem->NumParameterBlocks());  EXPECT_EQ(0, problem->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, problem->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, problem->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, problem->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, problem->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, problem->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, problem->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, problem->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, problem->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, problem->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, problem->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, problem->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, problem->NumResidualBlocks());  if (GetParam()) {    ExpectParameterBlockContains(y);    ExpectParameterBlockContains(z);    ExpectParameterBlockContains(w);  }}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 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);}}  // namespace internal}  // namespace ceres
 |