1 // Ceres Solver - A fast non-linear least squares minimizer 2 // Copyright 2010, 2011, 2012 Google Inc. All rights reserved. 3 // http://code.google.com/p/ceres-solver/ 4 // 5 // Redistribution and use in source and binary forms, with or without 6 // modification, are permitted provided that the following conditions are met: 7 // 8 // * Redistributions of source code must retain the above copyright notice, 9 // this list of conditions and the following disclaimer. 10 // * Redistributions in binary form must reproduce the above copyright notice, 11 // this list of conditions and the following disclaimer in the documentation 12 // and/or other materials provided with the distribution. 13 // * Neither the name of Google Inc. nor the names of its contributors may be 14 // used to endorse or promote products derived from this software without 15 // specific prior written permission. 16 // 17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 // POSSIBILITY OF SUCH DAMAGE. 28 // 29 // Author: sameeragarwal (at) google.com (Sameer Agarwal) 30 // keir (at) google.com (Keir Mierle) 31 32 #include "ceres/problem.h" 33 #include "ceres/problem_impl.h" 34 35 #include "ceres/casts.h" 36 #include "ceres/cost_function.h" 37 #include "ceres/crs_matrix.h" 38 #include "ceres/evaluator_test_utils.cc" 39 #include "ceres/internal/eigen.h" 40 #include "ceres/internal/scoped_ptr.h" 41 #include "ceres/local_parameterization.h" 42 #include "ceres/map_util.h" 43 #include "ceres/parameter_block.h" 44 #include "ceres/program.h" 45 #include "ceres/sized_cost_function.h" 46 #include "ceres/sparse_matrix.h" 47 #include "ceres/types.h" 48 #include "gtest/gtest.h" 49 50 namespace ceres { 51 namespace internal { 52 53 // The following three classes are for the purposes of defining 54 // function signatures. They have dummy Evaluate functions. 55 56 // Trivial cost function that accepts a single argument. 57 class UnaryCostFunction : public CostFunction { 58 public: 59 UnaryCostFunction(int num_residuals, int16 parameter_block_size) { 60 set_num_residuals(num_residuals); 61 mutable_parameter_block_sizes()->push_back(parameter_block_size); 62 } 63 virtual ~UnaryCostFunction() {} 64 65 virtual bool Evaluate(double const* const* parameters, 66 double* residuals, 67 double** jacobians) const { 68 for (int i = 0; i < num_residuals(); ++i) { 69 residuals[i] = 1; 70 } 71 return true; 72 } 73 }; 74 75 // Trivial cost function that accepts two arguments. 76 class BinaryCostFunction: public CostFunction { 77 public: 78 BinaryCostFunction(int num_residuals, 79 int16 parameter_block1_size, 80 int16 parameter_block2_size) { 81 set_num_residuals(num_residuals); 82 mutable_parameter_block_sizes()->push_back(parameter_block1_size); 83 mutable_parameter_block_sizes()->push_back(parameter_block2_size); 84 } 85 86 virtual bool Evaluate(double const* const* parameters, 87 double* residuals, 88 double** jacobians) const { 89 for (int i = 0; i < num_residuals(); ++i) { 90 residuals[i] = 2; 91 } 92 return true; 93 } 94 }; 95 96 // Trivial cost function that accepts three arguments. 97 class TernaryCostFunction: public CostFunction { 98 public: 99 TernaryCostFunction(int num_residuals, 100 int16 parameter_block1_size, 101 int16 parameter_block2_size, 102 int16 parameter_block3_size) { 103 set_num_residuals(num_residuals); 104 mutable_parameter_block_sizes()->push_back(parameter_block1_size); 105 mutable_parameter_block_sizes()->push_back(parameter_block2_size); 106 mutable_parameter_block_sizes()->push_back(parameter_block3_size); 107 } 108 109 virtual bool Evaluate(double const* const* parameters, 110 double* residuals, 111 double** jacobians) const { 112 for (int i = 0; i < num_residuals(); ++i) { 113 residuals[i] = 3; 114 } 115 return true; 116 } 117 }; 118 119 TEST(Problem, AddResidualWithNullCostFunctionDies) { 120 double x[3], y[4], z[5]; 121 122 Problem problem; 123 problem.AddParameterBlock(x, 3); 124 problem.AddParameterBlock(y, 4); 125 problem.AddParameterBlock(z, 5); 126 127 EXPECT_DEATH_IF_SUPPORTED(problem.AddResidualBlock(NULL, NULL, x), 128 "'cost_function' Must be non NULL"); 129 } 130 131 TEST(Problem, AddResidualWithIncorrectNumberOfParameterBlocksDies) { 132 double x[3], y[4], z[5]; 133 134 Problem problem; 135 problem.AddParameterBlock(x, 3); 136 problem.AddParameterBlock(y, 4); 137 problem.AddParameterBlock(z, 5); 138 139 // UnaryCostFunction takes only one parameter, but two are passed. 140 EXPECT_DEATH_IF_SUPPORTED( 141 problem.AddResidualBlock(new UnaryCostFunction(2, 3), NULL, x, y), 142 "parameter_blocks.size()"); 143 } 144 145 TEST(Problem, AddResidualWithDifferentSizesOnTheSameVariableDies) { 146 double x[3]; 147 148 Problem problem; 149 problem.AddResidualBlock(new UnaryCostFunction(2, 3), NULL, x); 150 EXPECT_DEATH_IF_SUPPORTED(problem.AddResidualBlock( 151 new UnaryCostFunction( 152 2, 4 /* 4 != 3 */), NULL, x), 153 "different block sizes"); 154 } 155 156 TEST(Problem, AddResidualWithDuplicateParametersDies) { 157 double x[3], z[5]; 158 159 Problem problem; 160 EXPECT_DEATH_IF_SUPPORTED(problem.AddResidualBlock( 161 new BinaryCostFunction(2, 3, 3), NULL, x, x), 162 "Duplicate parameter blocks"); 163 EXPECT_DEATH_IF_SUPPORTED(problem.AddResidualBlock( 164 new TernaryCostFunction(1, 5, 3, 5), 165 NULL, z, x, z), 166 "Duplicate parameter blocks"); 167 } 168 169 TEST(Problem, AddResidualWithIncorrectSizesOfParameterBlockDies) { 170 double x[3], y[4], z[5]; 171 172 Problem problem; 173 problem.AddParameterBlock(x, 3); 174 problem.AddParameterBlock(y, 4); 175 problem.AddParameterBlock(z, 5); 176 177 // The cost function expects the size of the second parameter, z, to be 4 178 // instead of 5 as declared above. This is fatal. 179 EXPECT_DEATH_IF_SUPPORTED(problem.AddResidualBlock( 180 new BinaryCostFunction(2, 3, 4), NULL, x, z), 181 "different block sizes"); 182 } 183 184 TEST(Problem, AddResidualAddsDuplicatedParametersOnlyOnce) { 185 double x[3], y[4], z[5]; 186 187 Problem problem; 188 problem.AddResidualBlock(new UnaryCostFunction(2, 3), NULL, x); 189 problem.AddResidualBlock(new UnaryCostFunction(2, 3), NULL, x); 190 problem.AddResidualBlock(new UnaryCostFunction(2, 4), NULL, y); 191 problem.AddResidualBlock(new UnaryCostFunction(2, 5), NULL, z); 192 193 EXPECT_EQ(3, problem.NumParameterBlocks()); 194 EXPECT_EQ(12, problem.NumParameters()); 195 } 196 197 TEST(Problem, AddParameterWithDifferentSizesOnTheSameVariableDies) { 198 double x[3], y[4]; 199 200 Problem problem; 201 problem.AddParameterBlock(x, 3); 202 problem.AddParameterBlock(y, 4); 203 204 EXPECT_DEATH_IF_SUPPORTED(problem.AddParameterBlock(x, 4), 205 "different block sizes"); 206 } 207 208 static double *IntToPtr(int i) { 209 return reinterpret_cast<double*>(sizeof(double) * i); // NOLINT 210 } 211 212 TEST(Problem, AddParameterWithAliasedParametersDies) { 213 // Layout is 214 // 215 // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 216 // [x] x x x x [y] y y 217 // o==o==o o==o==o o==o 218 // o--o--o o--o--o o--o o--o--o 219 // 220 // Parameter block additions are tested as listed above; expected successful 221 // ones marked with o==o and aliasing ones marked with o--o. 222 223 Problem problem; 224 problem.AddParameterBlock(IntToPtr(5), 5); // x 225 problem.AddParameterBlock(IntToPtr(13), 3); // y 226 227 EXPECT_DEATH_IF_SUPPORTED(problem.AddParameterBlock(IntToPtr( 4), 2), 228 "Aliasing detected"); 229 EXPECT_DEATH_IF_SUPPORTED(problem.AddParameterBlock(IntToPtr( 4), 3), 230 "Aliasing detected"); 231 EXPECT_DEATH_IF_SUPPORTED(problem.AddParameterBlock(IntToPtr( 4), 9), 232 "Aliasing detected"); 233 EXPECT_DEATH_IF_SUPPORTED(problem.AddParameterBlock(IntToPtr( 8), 3), 234 "Aliasing detected"); 235 EXPECT_DEATH_IF_SUPPORTED(problem.AddParameterBlock(IntToPtr(12), 2), 236 "Aliasing detected"); 237 EXPECT_DEATH_IF_SUPPORTED(problem.AddParameterBlock(IntToPtr(14), 3), 238 "Aliasing detected"); 239 240 // These ones should work. 241 problem.AddParameterBlock(IntToPtr( 2), 3); 242 problem.AddParameterBlock(IntToPtr(10), 3); 243 problem.AddParameterBlock(IntToPtr(16), 2); 244 245 ASSERT_EQ(5, problem.NumParameterBlocks()); 246 } 247 248 TEST(Problem, AddParameterIgnoresDuplicateCalls) { 249 double x[3], y[4]; 250 251 Problem problem; 252 problem.AddParameterBlock(x, 3); 253 problem.AddParameterBlock(y, 4); 254 255 // Creating parameter blocks multiple times is ignored. 256 problem.AddParameterBlock(x, 3); 257 problem.AddResidualBlock(new UnaryCostFunction(2, 3), NULL, x); 258 259 // ... even repeatedly. 260 problem.AddParameterBlock(x, 3); 261 problem.AddResidualBlock(new UnaryCostFunction(2, 3), NULL, x); 262 263 // More parameters are fine. 264 problem.AddParameterBlock(y, 4); 265 problem.AddResidualBlock(new UnaryCostFunction(2, 4), NULL, y); 266 267 EXPECT_EQ(2, problem.NumParameterBlocks()); 268 EXPECT_EQ(7, problem.NumParameters()); 269 } 270 271 TEST(Problem, AddingParametersAndResidualsResultsInExpectedProblem) { 272 double x[3], y[4], z[5], w[4]; 273 274 Problem problem; 275 problem.AddParameterBlock(x, 3); 276 EXPECT_EQ(1, problem.NumParameterBlocks()); 277 EXPECT_EQ(3, problem.NumParameters()); 278 279 problem.AddParameterBlock(y, 4); 280 EXPECT_EQ(2, problem.NumParameterBlocks()); 281 EXPECT_EQ(7, problem.NumParameters()); 282 283 problem.AddParameterBlock(z, 5); 284 EXPECT_EQ(3, problem.NumParameterBlocks()); 285 EXPECT_EQ(12, problem.NumParameters()); 286 287 // Add a parameter that has a local parameterization. 288 w[0] = 1.0; w[1] = 0.0; w[2] = 0.0; w[3] = 0.0; 289 problem.AddParameterBlock(w, 4, new QuaternionParameterization); 290 EXPECT_EQ(4, problem.NumParameterBlocks()); 291 EXPECT_EQ(16, problem.NumParameters()); 292 293 problem.AddResidualBlock(new UnaryCostFunction(2, 3), NULL, x); 294 problem.AddResidualBlock(new BinaryCostFunction(6, 5, 4) , NULL, z, y); 295 problem.AddResidualBlock(new BinaryCostFunction(3, 3, 5), NULL, x, z); 296 problem.AddResidualBlock(new BinaryCostFunction(7, 5, 3), NULL, z, x); 297 problem.AddResidualBlock(new TernaryCostFunction(1, 5, 3, 4), NULL, z, x, y); 298 299 const int total_residuals = 2 + 6 + 3 + 7 + 1; 300 EXPECT_EQ(problem.NumResidualBlocks(), 5); 301 EXPECT_EQ(problem.NumResiduals(), total_residuals); 302 } 303 304 class DestructorCountingCostFunction : public SizedCostFunction<3, 4, 5> { 305 public: 306 explicit DestructorCountingCostFunction(int *num_destructions) 307 : num_destructions_(num_destructions) {} 308 309 virtual ~DestructorCountingCostFunction() { 310 *num_destructions_ += 1; 311 } 312 313 virtual bool Evaluate(double const* const* parameters, 314 double* residuals, 315 double** jacobians) const { 316 return true; 317 } 318 319 private: 320 int* num_destructions_; 321 }; 322 323 TEST(Problem, ReusedCostFunctionsAreOnlyDeletedOnce) { 324 double y[4], z[5]; 325 int num_destructions = 0; 326 327 // Add a cost function multiple times and check to make sure that 328 // the destructor on the cost function is only called once. 329 { 330 Problem problem; 331 problem.AddParameterBlock(y, 4); 332 problem.AddParameterBlock(z, 5); 333 334 CostFunction* cost = new DestructorCountingCostFunction(&num_destructions); 335 problem.AddResidualBlock(cost, NULL, y, z); 336 problem.AddResidualBlock(cost, NULL, y, z); 337 problem.AddResidualBlock(cost, NULL, y, z); 338 EXPECT_EQ(3, problem.NumResidualBlocks()); 339 } 340 341 // Check that the destructor was called only once. 342 CHECK_EQ(num_destructions, 1); 343 } 344 345 TEST(Problem, CostFunctionsAreDeletedEvenWithRemovals) { 346 double y[4], z[5], w[4]; 347 int num_destructions = 0; 348 { 349 Problem problem; 350 problem.AddParameterBlock(y, 4); 351 problem.AddParameterBlock(z, 5); 352 353 CostFunction* cost_yz = 354 new DestructorCountingCostFunction(&num_destructions); 355 CostFunction* cost_wz = 356 new DestructorCountingCostFunction(&num_destructions); 357 ResidualBlock* r_yz = problem.AddResidualBlock(cost_yz, NULL, y, z); 358 ResidualBlock* r_wz = problem.AddResidualBlock(cost_wz, NULL, w, z); 359 EXPECT_EQ(2, problem.NumResidualBlocks()); 360 361 // In the current implementation, the destructor shouldn't get run yet. 362 problem.RemoveResidualBlock(r_yz); 363 CHECK_EQ(num_destructions, 0); 364 problem.RemoveResidualBlock(r_wz); 365 CHECK_EQ(num_destructions, 0); 366 367 EXPECT_EQ(0, problem.NumResidualBlocks()); 368 } 369 CHECK_EQ(num_destructions, 2); 370 } 371 372 // Make the dynamic problem tests (e.g. for removing residual blocks) 373 // parameterized on whether the low-latency mode is enabled or not. 374 // 375 // This tests against ProblemImpl instead of Problem in order to inspect the 376 // state of the resulting Program; this is difficult with only the thin Problem 377 // interface. 378 struct DynamicProblem : public ::testing::TestWithParam<bool> { 379 DynamicProblem() { 380 Problem::Options options; 381 options.enable_fast_parameter_block_removal = GetParam(); 382 problem.reset(new ProblemImpl(options)); 383 } 384 385 ParameterBlock* GetParameterBlock(int block) { 386 return problem->program().parameter_blocks()[block]; 387 } 388 ResidualBlock* GetResidualBlock(int block) { 389 return problem->program().residual_blocks()[block]; 390 } 391 392 bool HasResidualBlock(ResidualBlock* residual_block) { 393 return find(problem->program().residual_blocks().begin(), 394 problem->program().residual_blocks().end(), 395 residual_block) != problem->program().residual_blocks().end(); 396 } 397 398 // The next block of functions until the end are only for testing the 399 // residual block removals. 400 void ExpectParameterBlockContainsResidualBlock( 401 double* values, 402 ResidualBlock* residual_block) { 403 ParameterBlock* parameter_block = 404 FindOrDie(problem->parameter_map(), values); 405 EXPECT_TRUE(ContainsKey(*(parameter_block->mutable_residual_blocks()), 406 residual_block)); 407 } 408 409 void ExpectSize(double* values, int size) { 410 ParameterBlock* parameter_block = 411 FindOrDie(problem->parameter_map(), values); 412 EXPECT_EQ(size, parameter_block->mutable_residual_blocks()->size()); 413 } 414 415 // Degenerate case. 416 void ExpectParameterBlockContains(double* values) { 417 ExpectSize(values, 0); 418 } 419 420 void ExpectParameterBlockContains(double* values, 421 ResidualBlock* r1) { 422 ExpectSize(values, 1); 423 ExpectParameterBlockContainsResidualBlock(values, r1); 424 } 425 426 void ExpectParameterBlockContains(double* values, 427 ResidualBlock* r1, 428 ResidualBlock* r2) { 429 ExpectSize(values, 2); 430 ExpectParameterBlockContainsResidualBlock(values, r1); 431 ExpectParameterBlockContainsResidualBlock(values, r2); 432 } 433 434 void ExpectParameterBlockContains(double* values, 435 ResidualBlock* r1, 436 ResidualBlock* r2, 437 ResidualBlock* r3) { 438 ExpectSize(values, 3); 439 ExpectParameterBlockContainsResidualBlock(values, r1); 440 ExpectParameterBlockContainsResidualBlock(values, r2); 441 ExpectParameterBlockContainsResidualBlock(values, r3); 442 } 443 444 void ExpectParameterBlockContains(double* values, 445 ResidualBlock* r1, 446 ResidualBlock* r2, 447 ResidualBlock* r3, 448 ResidualBlock* r4) { 449 ExpectSize(values, 4); 450 ExpectParameterBlockContainsResidualBlock(values, r1); 451 ExpectParameterBlockContainsResidualBlock(values, r2); 452 ExpectParameterBlockContainsResidualBlock(values, r3); 453 ExpectParameterBlockContainsResidualBlock(values, r4); 454 } 455 456 scoped_ptr<ProblemImpl> problem; 457 double y[4], z[5], w[3]; 458 }; 459 460 TEST(Problem, SetParameterBlockConstantWithUnknownPtrDies) { 461 double x[3]; 462 double y[2]; 463 464 Problem problem; 465 problem.AddParameterBlock(x, 3); 466 467 EXPECT_DEATH_IF_SUPPORTED(problem.SetParameterBlockConstant(y), 468 "Parameter block not found:"); 469 } 470 471 TEST(Problem, SetParameterBlockVariableWithUnknownPtrDies) { 472 double x[3]; 473 double y[2]; 474 475 Problem problem; 476 problem.AddParameterBlock(x, 3); 477 478 EXPECT_DEATH_IF_SUPPORTED(problem.SetParameterBlockVariable(y), 479 "Parameter block not found:"); 480 } 481 482 TEST(Problem, SetLocalParameterizationWithUnknownPtrDies) { 483 double x[3]; 484 double y[2]; 485 486 Problem problem; 487 problem.AddParameterBlock(x, 3); 488 489 EXPECT_DEATH_IF_SUPPORTED( 490 problem.SetParameterization(y, new IdentityParameterization(3)), 491 "Parameter block not found:"); 492 } 493 494 TEST(Problem, RemoveParameterBlockWithUnknownPtrDies) { 495 double x[3]; 496 double y[2]; 497 498 Problem problem; 499 problem.AddParameterBlock(x, 3); 500 501 EXPECT_DEATH_IF_SUPPORTED( 502 problem.RemoveParameterBlock(y), "Parameter block not found:"); 503 } 504 505 TEST(Problem, ParameterBlockQueryTest) { 506 double x[3]; 507 double y[4]; 508 Problem problem; 509 problem.AddParameterBlock(x, 3); 510 problem.AddParameterBlock(y, 4); 511 512 vector<int> constant_parameters; 513 constant_parameters.push_back(0); 514 problem.SetParameterization( 515 x, 516 new SubsetParameterization(3, constant_parameters)); 517 EXPECT_EQ(problem.ParameterBlockSize(x), 3); 518 EXPECT_EQ(problem.ParameterBlockLocalSize(x), 2); 519 EXPECT_EQ(problem.ParameterBlockLocalSize(y), 4); 520 521 vector<double*> parameter_blocks; 522 problem.GetParameterBlocks(¶meter_blocks); 523 EXPECT_EQ(parameter_blocks.size(), 2); 524 EXPECT_NE(parameter_blocks[0], parameter_blocks[1]); 525 EXPECT_TRUE(parameter_blocks[0] == x || parameter_blocks[0] == y); 526 EXPECT_TRUE(parameter_blocks[1] == x || parameter_blocks[1] == y); 527 528 problem.RemoveParameterBlock(x); 529 problem.GetParameterBlocks(¶meter_blocks); 530 EXPECT_EQ(parameter_blocks.size(), 1); 531 EXPECT_TRUE(parameter_blocks[0] == y); 532 } 533 534 TEST_P(DynamicProblem, RemoveParameterBlockWithNoResiduals) { 535 problem->AddParameterBlock(y, 4); 536 problem->AddParameterBlock(z, 5); 537 problem->AddParameterBlock(w, 3); 538 ASSERT_EQ(3, problem->NumParameterBlocks()); 539 ASSERT_EQ(0, problem->NumResidualBlocks()); 540 EXPECT_EQ(y, GetParameterBlock(0)->user_state()); 541 EXPECT_EQ(z, GetParameterBlock(1)->user_state()); 542 EXPECT_EQ(w, GetParameterBlock(2)->user_state()); 543 544 // w is at the end, which might break the swapping logic so try adding and 545 // removing it. 546 problem->RemoveParameterBlock(w); 547 ASSERT_EQ(2, problem->NumParameterBlocks()); 548 ASSERT_EQ(0, problem->NumResidualBlocks()); 549 EXPECT_EQ(y, GetParameterBlock(0)->user_state()); 550 EXPECT_EQ(z, GetParameterBlock(1)->user_state()); 551 problem->AddParameterBlock(w, 3); 552 ASSERT_EQ(3, problem->NumParameterBlocks()); 553 ASSERT_EQ(0, problem->NumResidualBlocks()); 554 EXPECT_EQ(y, GetParameterBlock(0)->user_state()); 555 EXPECT_EQ(z, GetParameterBlock(1)->user_state()); 556 EXPECT_EQ(w, GetParameterBlock(2)->user_state()); 557 558 // Now remove z, which is in the middle, and add it back. 559 problem->RemoveParameterBlock(z); 560 ASSERT_EQ(2, problem->NumParameterBlocks()); 561 ASSERT_EQ(0, problem->NumResidualBlocks()); 562 EXPECT_EQ(y, GetParameterBlock(0)->user_state()); 563 EXPECT_EQ(w, GetParameterBlock(1)->user_state()); 564 problem->AddParameterBlock(z, 5); 565 ASSERT_EQ(3, problem->NumParameterBlocks()); 566 ASSERT_EQ(0, problem->NumResidualBlocks()); 567 EXPECT_EQ(y, GetParameterBlock(0)->user_state()); 568 EXPECT_EQ(w, GetParameterBlock(1)->user_state()); 569 EXPECT_EQ(z, GetParameterBlock(2)->user_state()); 570 571 // Now remove everything. 572 // y 573 problem->RemoveParameterBlock(y); 574 ASSERT_EQ(2, problem->NumParameterBlocks()); 575 ASSERT_EQ(0, problem->NumResidualBlocks()); 576 EXPECT_EQ(z, GetParameterBlock(0)->user_state()); 577 EXPECT_EQ(w, GetParameterBlock(1)->user_state()); 578 579 // z 580 problem->RemoveParameterBlock(z); 581 ASSERT_EQ(1, problem->NumParameterBlocks()); 582 ASSERT_EQ(0, problem->NumResidualBlocks()); 583 EXPECT_EQ(w, GetParameterBlock(0)->user_state()); 584 585 // w 586 problem->RemoveParameterBlock(w); 587 EXPECT_EQ(0, problem->NumParameterBlocks()); 588 EXPECT_EQ(0, problem->NumResidualBlocks()); 589 } 590 591 TEST_P(DynamicProblem, RemoveParameterBlockWithResiduals) { 592 problem->AddParameterBlock(y, 4); 593 problem->AddParameterBlock(z, 5); 594 problem->AddParameterBlock(w, 3); 595 ASSERT_EQ(3, problem->NumParameterBlocks()); 596 ASSERT_EQ(0, problem->NumResidualBlocks()); 597 EXPECT_EQ(y, GetParameterBlock(0)->user_state()); 598 EXPECT_EQ(z, GetParameterBlock(1)->user_state()); 599 EXPECT_EQ(w, GetParameterBlock(2)->user_state()); 600 601 // Add all combinations of cost functions. 602 CostFunction* cost_yzw = new TernaryCostFunction(1, 4, 5, 3); 603 CostFunction* cost_yz = new BinaryCostFunction (1, 4, 5); 604 CostFunction* cost_yw = new BinaryCostFunction (1, 4, 3); 605 CostFunction* cost_zw = new BinaryCostFunction (1, 5, 3); 606 CostFunction* cost_y = new UnaryCostFunction (1, 4); 607 CostFunction* cost_z = new UnaryCostFunction (1, 5); 608 CostFunction* cost_w = new UnaryCostFunction (1, 3); 609 610 ResidualBlock* r_yzw = problem->AddResidualBlock(cost_yzw, NULL, y, z, w); 611 ResidualBlock* r_yz = problem->AddResidualBlock(cost_yz, NULL, y, z); 612 ResidualBlock* r_yw = problem->AddResidualBlock(cost_yw, NULL, y, w); 613 ResidualBlock* r_zw = problem->AddResidualBlock(cost_zw, NULL, z, w); 614 ResidualBlock* r_y = problem->AddResidualBlock(cost_y, NULL, y); 615 ResidualBlock* r_z = problem->AddResidualBlock(cost_z, NULL, z); 616 ResidualBlock* r_w = problem->AddResidualBlock(cost_w, NULL, w); 617 618 EXPECT_EQ(3, problem->NumParameterBlocks()); 619 EXPECT_EQ(7, problem->NumResidualBlocks()); 620 621 // Remove w, which should remove r_yzw, r_yw, r_zw, r_w. 622 problem->RemoveParameterBlock(w); 623 ASSERT_EQ(2, problem->NumParameterBlocks()); 624 ASSERT_EQ(3, problem->NumResidualBlocks()); 625 626 ASSERT_FALSE(HasResidualBlock(r_yzw)); 627 ASSERT_TRUE (HasResidualBlock(r_yz )); 628 ASSERT_FALSE(HasResidualBlock(r_yw )); 629 ASSERT_FALSE(HasResidualBlock(r_zw )); 630 ASSERT_TRUE (HasResidualBlock(r_y )); 631 ASSERT_TRUE (HasResidualBlock(r_z )); 632 ASSERT_FALSE(HasResidualBlock(r_w )); 633 634 // Remove z, which will remove almost everything else. 635 problem->RemoveParameterBlock(z); 636 ASSERT_EQ(1, problem->NumParameterBlocks()); 637 ASSERT_EQ(1, problem->NumResidualBlocks()); 638 639 ASSERT_FALSE(HasResidualBlock(r_yzw)); 640 ASSERT_FALSE(HasResidualBlock(r_yz )); 641 ASSERT_FALSE(HasResidualBlock(r_yw )); 642 ASSERT_FALSE(HasResidualBlock(r_zw )); 643 ASSERT_TRUE (HasResidualBlock(r_y )); 644 ASSERT_FALSE(HasResidualBlock(r_z )); 645 ASSERT_FALSE(HasResidualBlock(r_w )); 646 647 // Remove y; all gone. 648 problem->RemoveParameterBlock(y); 649 EXPECT_EQ(0, problem->NumParameterBlocks()); 650 EXPECT_EQ(0, problem->NumResidualBlocks()); 651 } 652 653 TEST_P(DynamicProblem, RemoveResidualBlock) { 654 problem->AddParameterBlock(y, 4); 655 problem->AddParameterBlock(z, 5); 656 problem->AddParameterBlock(w, 3); 657 658 // Add all combinations of cost functions. 659 CostFunction* cost_yzw = new TernaryCostFunction(1, 4, 5, 3); 660 CostFunction* cost_yz = new BinaryCostFunction (1, 4, 5); 661 CostFunction* cost_yw = new BinaryCostFunction (1, 4, 3); 662 CostFunction* cost_zw = new BinaryCostFunction (1, 5, 3); 663 CostFunction* cost_y = new UnaryCostFunction (1, 4); 664 CostFunction* cost_z = new UnaryCostFunction (1, 5); 665 CostFunction* cost_w = new UnaryCostFunction (1, 3); 666 667 ResidualBlock* r_yzw = problem->AddResidualBlock(cost_yzw, NULL, y, z, w); 668 ResidualBlock* r_yz = problem->AddResidualBlock(cost_yz, NULL, y, z); 669 ResidualBlock* r_yw = problem->AddResidualBlock(cost_yw, NULL, y, w); 670 ResidualBlock* r_zw = problem->AddResidualBlock(cost_zw, NULL, z, w); 671 ResidualBlock* r_y = problem->AddResidualBlock(cost_y, NULL, y); 672 ResidualBlock* r_z = problem->AddResidualBlock(cost_z, NULL, z); 673 ResidualBlock* r_w = problem->AddResidualBlock(cost_w, NULL, w); 674 675 if (GetParam()) { 676 // In this test parameterization, there should be back-pointers from the 677 // parameter blocks to the residual blocks. 678 ExpectParameterBlockContains(y, r_yzw, r_yz, r_yw, r_y); 679 ExpectParameterBlockContains(z, r_yzw, r_yz, r_zw, r_z); 680 ExpectParameterBlockContains(w, r_yzw, r_yw, r_zw, r_w); 681 } else { 682 // Otherwise, nothing. 683 EXPECT_TRUE(GetParameterBlock(0)->mutable_residual_blocks() == NULL); 684 EXPECT_TRUE(GetParameterBlock(1)->mutable_residual_blocks() == NULL); 685 EXPECT_TRUE(GetParameterBlock(2)->mutable_residual_blocks() == NULL); 686 } 687 EXPECT_EQ(3, problem->NumParameterBlocks()); 688 EXPECT_EQ(7, problem->NumResidualBlocks()); 689 690 // Remove each residual and check the state after each removal. 691 692 // Remove r_yzw. 693 problem->RemoveResidualBlock(r_yzw); 694 ASSERT_EQ(3, problem->NumParameterBlocks()); 695 ASSERT_EQ(6, problem->NumResidualBlocks()); 696 if (GetParam()) { 697 ExpectParameterBlockContains(y, r_yz, r_yw, r_y); 698 ExpectParameterBlockContains(z, r_yz, r_zw, r_z); 699 ExpectParameterBlockContains(w, r_yw, r_zw, r_w); 700 } 701 ASSERT_TRUE (HasResidualBlock(r_yz )); 702 ASSERT_TRUE (HasResidualBlock(r_yw )); 703 ASSERT_TRUE (HasResidualBlock(r_zw )); 704 ASSERT_TRUE (HasResidualBlock(r_y )); 705 ASSERT_TRUE (HasResidualBlock(r_z )); 706 ASSERT_TRUE (HasResidualBlock(r_w )); 707 708 // Remove r_yw. 709 problem->RemoveResidualBlock(r_yw); 710 ASSERT_EQ(3, problem->NumParameterBlocks()); 711 ASSERT_EQ(5, problem->NumResidualBlocks()); 712 if (GetParam()) { 713 ExpectParameterBlockContains(y, r_yz, r_y); 714 ExpectParameterBlockContains(z, r_yz, r_zw, r_z); 715 ExpectParameterBlockContains(w, r_zw, r_w); 716 } 717 ASSERT_TRUE (HasResidualBlock(r_yz )); 718 ASSERT_TRUE (HasResidualBlock(r_zw )); 719 ASSERT_TRUE (HasResidualBlock(r_y )); 720 ASSERT_TRUE (HasResidualBlock(r_z )); 721 ASSERT_TRUE (HasResidualBlock(r_w )); 722 723 // Remove r_zw. 724 problem->RemoveResidualBlock(r_zw); 725 ASSERT_EQ(3, problem->NumParameterBlocks()); 726 ASSERT_EQ(4, problem->NumResidualBlocks()); 727 if (GetParam()) { 728 ExpectParameterBlockContains(y, r_yz, r_y); 729 ExpectParameterBlockContains(z, r_yz, r_z); 730 ExpectParameterBlockContains(w, r_w); 731 } 732 ASSERT_TRUE (HasResidualBlock(r_yz )); 733 ASSERT_TRUE (HasResidualBlock(r_y )); 734 ASSERT_TRUE (HasResidualBlock(r_z )); 735 ASSERT_TRUE (HasResidualBlock(r_w )); 736 737 // Remove r_w. 738 problem->RemoveResidualBlock(r_w); 739 ASSERT_EQ(3, problem->NumParameterBlocks()); 740 ASSERT_EQ(3, problem->NumResidualBlocks()); 741 if (GetParam()) { 742 ExpectParameterBlockContains(y, r_yz, r_y); 743 ExpectParameterBlockContains(z, r_yz, r_z); 744 ExpectParameterBlockContains(w); 745 } 746 ASSERT_TRUE (HasResidualBlock(r_yz )); 747 ASSERT_TRUE (HasResidualBlock(r_y )); 748 ASSERT_TRUE (HasResidualBlock(r_z )); 749 750 // Remove r_yz. 751 problem->RemoveResidualBlock(r_yz); 752 ASSERT_EQ(3, problem->NumParameterBlocks()); 753 ASSERT_EQ(2, problem->NumResidualBlocks()); 754 if (GetParam()) { 755 ExpectParameterBlockContains(y, r_y); 756 ExpectParameterBlockContains(z, r_z); 757 ExpectParameterBlockContains(w); 758 } 759 ASSERT_TRUE (HasResidualBlock(r_y )); 760 ASSERT_TRUE (HasResidualBlock(r_z )); 761 762 // Remove the last two. 763 problem->RemoveResidualBlock(r_z); 764 problem->RemoveResidualBlock(r_y); 765 ASSERT_EQ(3, problem->NumParameterBlocks()); 766 ASSERT_EQ(0, problem->NumResidualBlocks()); 767 if (GetParam()) { 768 ExpectParameterBlockContains(y); 769 ExpectParameterBlockContains(z); 770 ExpectParameterBlockContains(w); 771 } 772 } 773 774 INSTANTIATE_TEST_CASE_P(OptionsInstantiation, 775 DynamicProblem, 776 ::testing::Values(true, false)); 777 778 // Test for Problem::Evaluate 779 780 // r_i = i - (j + 1) * x_ij^2 781 template <int kNumResiduals, int kNumParameterBlocks> 782 class QuadraticCostFunction : public CostFunction { 783 public: 784 QuadraticCostFunction() { 785 CHECK_GT(kNumResiduals, 0); 786 CHECK_GT(kNumParameterBlocks, 0); 787 set_num_residuals(kNumResiduals); 788 for (int i = 0; i < kNumParameterBlocks; ++i) { 789 mutable_parameter_block_sizes()->push_back(kNumResiduals); 790 } 791 } 792 793 virtual bool Evaluate(double const* const* parameters, 794 double* residuals, 795 double** jacobians) const { 796 for (int i = 0; i < kNumResiduals; ++i) { 797 residuals[i] = i; 798 for (int j = 0; j < kNumParameterBlocks; ++j) { 799 residuals[i] -= (j + 1.0) * parameters[j][i] * parameters[j][i]; 800 } 801 } 802 803 if (jacobians == NULL) { 804 return true; 805 } 806 807 for (int j = 0; j < kNumParameterBlocks; ++j) { 808 if (jacobians[j] != NULL) { 809 MatrixRef(jacobians[j], kNumResiduals, kNumResiduals) = 810 (-2.0 * (j + 1.0) * 811 ConstVectorRef(parameters[j], kNumResiduals)).asDiagonal(); 812 } 813 } 814 815 return true; 816 } 817 }; 818 819 // Convert a CRSMatrix to a dense Eigen matrix. 820 void CRSToDenseMatrix(const CRSMatrix& input, Matrix* output) { 821 Matrix& m = *CHECK_NOTNULL(output); 822 m.resize(input.num_rows, input.num_cols); 823 m.setZero(); 824 for (int row = 0; row < input.num_rows; ++row) { 825 for (int j = input.rows[row]; j < input.rows[row + 1]; ++j) { 826 const int col = input.cols[j]; 827 m(row, col) = input.values[j]; 828 } 829 } 830 } 831 832 class ProblemEvaluateTest : public ::testing::Test { 833 protected: 834 void SetUp() { 835 for (int i = 0; i < 6; ++i) { 836 parameters_[i] = static_cast<double>(i + 1); 837 } 838 839 parameter_blocks_.push_back(parameters_); 840 parameter_blocks_.push_back(parameters_ + 2); 841 parameter_blocks_.push_back(parameters_ + 4); 842 843 844 CostFunction* cost_function = new QuadraticCostFunction<2, 2>; 845 846 // f(x, y) 847 residual_blocks_.push_back( 848 problem_.AddResidualBlock(cost_function, 849 NULL, 850 parameters_, 851 parameters_ + 2)); 852 // g(y, z) 853 residual_blocks_.push_back( 854 problem_.AddResidualBlock(cost_function, 855 NULL, parameters_ + 2, 856 parameters_ + 4)); 857 // h(z, x) 858 residual_blocks_.push_back( 859 problem_.AddResidualBlock(cost_function, 860 NULL, 861 parameters_ + 4, 862 parameters_)); 863 } 864 865 866 867 void EvaluateAndCompare(const Problem::EvaluateOptions& options, 868 const int expected_num_rows, 869 const int expected_num_cols, 870 const double expected_cost, 871 const double* expected_residuals, 872 const double* expected_gradient, 873 const double* expected_jacobian) { 874 double cost; 875 vector<double> residuals; 876 vector<double> gradient; 877 CRSMatrix jacobian; 878 879 EXPECT_TRUE( 880 problem_.Evaluate(options, 881 &cost, 882 expected_residuals != NULL ? &residuals : NULL, 883 expected_gradient != NULL ? &gradient : NULL, 884 expected_jacobian != NULL ? &jacobian : NULL)); 885 886 if (expected_residuals != NULL) { 887 EXPECT_EQ(residuals.size(), expected_num_rows); 888 } 889 890 if (expected_gradient != NULL) { 891 EXPECT_EQ(gradient.size(), expected_num_cols); 892 } 893 894 if (expected_jacobian != NULL) { 895 EXPECT_EQ(jacobian.num_rows, expected_num_rows); 896 EXPECT_EQ(jacobian.num_cols, expected_num_cols); 897 } 898 899 Matrix dense_jacobian; 900 if (expected_jacobian != NULL) { 901 CRSToDenseMatrix(jacobian, &dense_jacobian); 902 } 903 904 CompareEvaluations(expected_num_rows, 905 expected_num_cols, 906 expected_cost, 907 expected_residuals, 908 expected_gradient, 909 expected_jacobian, 910 cost, 911 residuals.size() > 0 ? &residuals[0] : NULL, 912 gradient.size() > 0 ? &gradient[0] : NULL, 913 dense_jacobian.data()); 914 } 915 916 void CheckAllEvaluationCombinations(const Problem::EvaluateOptions& options, 917 const ExpectedEvaluation& expected) { 918 for (int i = 0; i < 8; ++i) { 919 EvaluateAndCompare(options, 920 expected.num_rows, 921 expected.num_cols, 922 expected.cost, 923 (i & 1) ? expected.residuals : NULL, 924 (i & 2) ? expected.gradient : NULL, 925 (i & 4) ? expected.jacobian : NULL); 926 } 927 } 928 929 ProblemImpl problem_; 930 double parameters_[6]; 931 vector<double*> parameter_blocks_; 932 vector<ResidualBlockId> residual_blocks_; 933 }; 934 935 936 TEST_F(ProblemEvaluateTest, MultipleParameterAndResidualBlocks) { 937 ExpectedEvaluation expected = { 938 // Rows/columns 939 6, 6, 940 // Cost 941 7607.0, 942 // Residuals 943 { -19.0, -35.0, // f 944 -59.0, -87.0, // g 945 -27.0, -43.0 // h 946 }, 947 // Gradient 948 { 146.0, 484.0, // x 949 582.0, 1256.0, // y 950 1450.0, 2604.0, // z 951 }, 952 // Jacobian 953 // x y z 954 { /* f(x, y) */ -2.0, 0.0, -12.0, 0.0, 0.0, 0.0, 955 0.0, -4.0, 0.0, -16.0, 0.0, 0.0, 956 /* g(y, z) */ 0.0, 0.0, -6.0, 0.0, -20.0, 0.0, 957 0.0, 0.0, 0.0, -8.0, 0.0, -24.0, 958 /* h(z, x) */ -4.0, 0.0, 0.0, 0.0, -10.0, 0.0, 959 0.0, -8.0, 0.0, 0.0, 0.0, -12.0 960 } 961 }; 962 963 CheckAllEvaluationCombinations(Problem::EvaluateOptions(), expected); 964 } 965 966 TEST_F(ProblemEvaluateTest, ParameterAndResidualBlocksPassedInOptions) { 967 ExpectedEvaluation expected = { 968 // Rows/columns 969 6, 6, 970 // Cost 971 7607.0, 972 // Residuals 973 { -19.0, -35.0, // f 974 -59.0, -87.0, // g 975 -27.0, -43.0 // h 976 }, 977 // Gradient 978 { 146.0, 484.0, // x 979 582.0, 1256.0, // y 980 1450.0, 2604.0, // z 981 }, 982 // Jacobian 983 // x y z 984 { /* f(x, y) */ -2.0, 0.0, -12.0, 0.0, 0.0, 0.0, 985 0.0, -4.0, 0.0, -16.0, 0.0, 0.0, 986 /* g(y, z) */ 0.0, 0.0, -6.0, 0.0, -20.0, 0.0, 987 0.0, 0.0, 0.0, -8.0, 0.0, -24.0, 988 /* h(z, x) */ -4.0, 0.0, 0.0, 0.0, -10.0, 0.0, 989 0.0, -8.0, 0.0, 0.0, 0.0, -12.0 990 } 991 }; 992 993 Problem::EvaluateOptions evaluate_options; 994 evaluate_options.parameter_blocks = parameter_blocks_; 995 evaluate_options.residual_blocks = residual_blocks_; 996 CheckAllEvaluationCombinations(evaluate_options, expected); 997 } 998 999 TEST_F(ProblemEvaluateTest, ReorderedResidualBlocks) { 1000 ExpectedEvaluation expected = { 1001 // Rows/columns 1002 6, 6, 1003 // Cost 1004 7607.0, 1005 // Residuals 1006 { -19.0, -35.0, // f 1007 -27.0, -43.0, // h 1008 -59.0, -87.0 // g 1009 }, 1010 // Gradient 1011 { 146.0, 484.0, // x 1012 582.0, 1256.0, // y 1013 1450.0, 2604.0, // z 1014 }, 1015 // Jacobian 1016 // x y z 1017 { /* f(x, y) */ -2.0, 0.0, -12.0, 0.0, 0.0, 0.0, 1018 0.0, -4.0, 0.0, -16.0, 0.0, 0.0, 1019 /* h(z, x) */ -4.0, 0.0, 0.0, 0.0, -10.0, 0.0, 1020 0.0, -8.0, 0.0, 0.0, 0.0, -12.0, 1021 /* g(y, z) */ 0.0, 0.0, -6.0, 0.0, -20.0, 0.0, 1022 0.0, 0.0, 0.0, -8.0, 0.0, -24.0 1023 } 1024 }; 1025 1026 Problem::EvaluateOptions evaluate_options; 1027 evaluate_options.parameter_blocks = parameter_blocks_; 1028 1029 // f, h, g 1030 evaluate_options.residual_blocks.push_back(residual_blocks_[0]); 1031 evaluate_options.residual_blocks.push_back(residual_blocks_[2]); 1032 evaluate_options.residual_blocks.push_back(residual_blocks_[1]); 1033 1034 CheckAllEvaluationCombinations(evaluate_options, expected); 1035 } 1036 1037 TEST_F(ProblemEvaluateTest, ReorderedResidualBlocksAndReorderedParameterBlocks) { 1038 ExpectedEvaluation expected = { 1039 // Rows/columns 1040 6, 6, 1041 // Cost 1042 7607.0, 1043 // Residuals 1044 { -19.0, -35.0, // f 1045 -27.0, -43.0, // h 1046 -59.0, -87.0 // g 1047 }, 1048 // Gradient 1049 { 1450.0, 2604.0, // z 1050 582.0, 1256.0, // y 1051 146.0, 484.0, // x 1052 }, 1053 // Jacobian 1054 // z y x 1055 { /* f(x, y) */ 0.0, 0.0, -12.0, 0.0, -2.0, 0.0, 1056 0.0, 0.0, 0.0, -16.0, 0.0, -4.0, 1057 /* h(z, x) */ -10.0, 0.0, 0.0, 0.0, -4.0, 0.0, 1058 0.0, -12.0, 0.0, 0.0, 0.0, -8.0, 1059 /* g(y, z) */ -20.0, 0.0, -6.0, 0.0, 0.0, 0.0, 1060 0.0, -24.0, 0.0, -8.0, 0.0, 0.0 1061 } 1062 }; 1063 1064 Problem::EvaluateOptions evaluate_options; 1065 // z, y, x 1066 evaluate_options.parameter_blocks.push_back(parameter_blocks_[2]); 1067 evaluate_options.parameter_blocks.push_back(parameter_blocks_[1]); 1068 evaluate_options.parameter_blocks.push_back(parameter_blocks_[0]); 1069 1070 // f, h, g 1071 evaluate_options.residual_blocks.push_back(residual_blocks_[0]); 1072 evaluate_options.residual_blocks.push_back(residual_blocks_[2]); 1073 evaluate_options.residual_blocks.push_back(residual_blocks_[1]); 1074 1075 CheckAllEvaluationCombinations(evaluate_options, expected); 1076 } 1077 1078 TEST_F(ProblemEvaluateTest, ConstantParameterBlock) { 1079 ExpectedEvaluation expected = { 1080 // Rows/columns 1081 6, 6, 1082 // Cost 1083 7607.0, 1084 // Residuals 1085 { -19.0, -35.0, // f 1086 -59.0, -87.0, // g 1087 -27.0, -43.0 // h 1088 }, 1089 1090 // Gradient 1091 { 146.0, 484.0, // x 1092 0.0, 0.0, // y 1093 1450.0, 2604.0, // z 1094 }, 1095 1096 // Jacobian 1097 // x y z 1098 { /* f(x, y) */ -2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1099 0.0, -4.0, 0.0, 0.0, 0.0, 0.0, 1100 /* g(y, z) */ 0.0, 0.0, 0.0, 0.0, -20.0, 0.0, 1101 0.0, 0.0, 0.0, 0.0, 0.0, -24.0, 1102 /* h(z, x) */ -4.0, 0.0, 0.0, 0.0, -10.0, 0.0, 1103 0.0, -8.0, 0.0, 0.0, 0.0, -12.0 1104 } 1105 }; 1106 1107 problem_.SetParameterBlockConstant(parameters_ + 2); 1108 CheckAllEvaluationCombinations(Problem::EvaluateOptions(), expected); 1109 } 1110 1111 TEST_F(ProblemEvaluateTest, ExcludedAResidualBlock) { 1112 ExpectedEvaluation expected = { 1113 // Rows/columns 1114 4, 6, 1115 // Cost 1116 2082.0, 1117 // Residuals 1118 { -19.0, -35.0, // f 1119 -27.0, -43.0 // h 1120 }, 1121 // Gradient 1122 { 146.0, 484.0, // x 1123 228.0, 560.0, // y 1124 270.0, 516.0, // z 1125 }, 1126 // Jacobian 1127 // x y z 1128 { /* f(x, y) */ -2.0, 0.0, -12.0, 0.0, 0.0, 0.0, 1129 0.0, -4.0, 0.0, -16.0, 0.0, 0.0, 1130 /* h(z, x) */ -4.0, 0.0, 0.0, 0.0, -10.0, 0.0, 1131 0.0, -8.0, 0.0, 0.0, 0.0, -12.0 1132 } 1133 }; 1134 1135 Problem::EvaluateOptions evaluate_options; 1136 evaluate_options.residual_blocks.push_back(residual_blocks_[0]); 1137 evaluate_options.residual_blocks.push_back(residual_blocks_[2]); 1138 1139 CheckAllEvaluationCombinations(evaluate_options, expected); 1140 } 1141 1142 TEST_F(ProblemEvaluateTest, ExcludedParameterBlock) { 1143 ExpectedEvaluation expected = { 1144 // Rows/columns 1145 6, 4, 1146 // Cost 1147 7607.0, 1148 // Residuals 1149 { -19.0, -35.0, // f 1150 -59.0, -87.0, // g 1151 -27.0, -43.0 // h 1152 }, 1153 1154 // Gradient 1155 { 146.0, 484.0, // x 1156 1450.0, 2604.0, // z 1157 }, 1158 1159 // Jacobian 1160 // x z 1161 { /* f(x, y) */ -2.0, 0.0, 0.0, 0.0, 1162 0.0, -4.0, 0.0, 0.0, 1163 /* g(y, z) */ 0.0, 0.0, -20.0, 0.0, 1164 0.0, 0.0, 0.0, -24.0, 1165 /* h(z, x) */ -4.0, 0.0, -10.0, 0.0, 1166 0.0, -8.0, 0.0, -12.0 1167 } 1168 }; 1169 1170 Problem::EvaluateOptions evaluate_options; 1171 // x, z 1172 evaluate_options.parameter_blocks.push_back(parameter_blocks_[0]); 1173 evaluate_options.parameter_blocks.push_back(parameter_blocks_[2]); 1174 evaluate_options.residual_blocks = residual_blocks_; 1175 CheckAllEvaluationCombinations(evaluate_options, expected); 1176 } 1177 1178 TEST_F(ProblemEvaluateTest, ExcludedParameterBlockAndExcludedResidualBlock) { 1179 ExpectedEvaluation expected = { 1180 // Rows/columns 1181 4, 4, 1182 // Cost 1183 6318.0, 1184 // Residuals 1185 { -19.0, -35.0, // f 1186 -59.0, -87.0, // g 1187 }, 1188 1189 // Gradient 1190 { 38.0, 140.0, // x 1191 1180.0, 2088.0, // z 1192 }, 1193 1194 // Jacobian 1195 // x z 1196 { /* f(x, y) */ -2.0, 0.0, 0.0, 0.0, 1197 0.0, -4.0, 0.0, 0.0, 1198 /* g(y, z) */ 0.0, 0.0, -20.0, 0.0, 1199 0.0, 0.0, 0.0, -24.0, 1200 } 1201 }; 1202 1203 Problem::EvaluateOptions evaluate_options; 1204 // x, z 1205 evaluate_options.parameter_blocks.push_back(parameter_blocks_[0]); 1206 evaluate_options.parameter_blocks.push_back(parameter_blocks_[2]); 1207 evaluate_options.residual_blocks.push_back(residual_blocks_[0]); 1208 evaluate_options.residual_blocks.push_back(residual_blocks_[1]); 1209 1210 CheckAllEvaluationCombinations(evaluate_options, expected); 1211 } 1212 1213 TEST_F(ProblemEvaluateTest, LocalParameterization) { 1214 ExpectedEvaluation expected = { 1215 // Rows/columns 1216 6, 5, 1217 // Cost 1218 7607.0, 1219 // Residuals 1220 { -19.0, -35.0, // f 1221 -59.0, -87.0, // g 1222 -27.0, -43.0 // h 1223 }, 1224 // Gradient 1225 { 146.0, 484.0, // x 1226 1256.0, // y with SubsetParameterization 1227 1450.0, 2604.0, // z 1228 }, 1229 // Jacobian 1230 // x y z 1231 { /* f(x, y) */ -2.0, 0.0, 0.0, 0.0, 0.0, 1232 0.0, -4.0, -16.0, 0.0, 0.0, 1233 /* g(y, z) */ 0.0, 0.0, 0.0, -20.0, 0.0, 1234 0.0, 0.0, -8.0, 0.0, -24.0, 1235 /* h(z, x) */ -4.0, 0.0, 0.0, -10.0, 0.0, 1236 0.0, -8.0, 0.0, 0.0, -12.0 1237 } 1238 }; 1239 1240 vector<int> constant_parameters; 1241 constant_parameters.push_back(0); 1242 problem_.SetParameterization(parameters_ + 2, 1243 new SubsetParameterization(2, 1244 constant_parameters)); 1245 1246 CheckAllEvaluationCombinations(Problem::EvaluateOptions(), expected); 1247 } 1248 1249 } // namespace internal 1250 } // namespace ceres 1251