| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334 | // 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 "gtest/gtest.h"#include "ceres/cost_function.h"#include "ceres/local_parameterization.h"#include "ceres/sized_cost_function.h"#include "ceres/internal/scoped_ptr.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;  }};// TODO(keir): Figure out how to enable death tests on Windows.#ifndef _WIN32TEST(Problem, AddResidualWithNullCostFunctionDies) {  double x[3], y[4], z[5];  Problem problem;  problem.AddParameterBlock(x, 3);  problem.AddParameterBlock(y, 4);  problem.AddParameterBlock(z, 5);  ASSERT_DEATH(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.  ASSERT_DEATH(      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);  ASSERT_DEATH(problem.AddResidualBlock(      new UnaryCostFunction(2, 4 /* 4 != 3 */), NULL, x),               "different block sizes");}TEST(Problem, AddResidualWithDuplicateParametersDies) {  double x[3], z[5];  Problem problem;  ASSERT_DEATH(problem.AddResidualBlock(      new BinaryCostFunction(2, 3, 3), NULL, x, x),               "Duplicate parameter blocks");  ASSERT_DEATH(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.  ASSERT_DEATH(problem.AddResidualBlock(      new BinaryCostFunction(2, 3, 4), NULL, x, z),               "different block sizes");}#endif  // _WIN32TEST(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());}#ifndef _WIN32TEST(Problem, AddParameterWithDifferentSizesOnTheSameVariableDies) {  double x[3], y[4];  Problem problem;  problem.AddParameterBlock(x, 3);  problem.AddParameterBlock(y, 4);  ASSERT_DEATH(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  ASSERT_DEATH(problem.AddParameterBlock(IntToPtr( 4), 2), "Aliasing detected");  ASSERT_DEATH(problem.AddParameterBlock(IntToPtr( 4), 3), "Aliasing detected");  ASSERT_DEATH(problem.AddParameterBlock(IntToPtr( 4), 9), "Aliasing detected");  ASSERT_DEATH(problem.AddParameterBlock(IntToPtr( 8), 3), "Aliasing detected");  ASSERT_DEATH(problem.AddParameterBlock(IntToPtr(12), 2), "Aliasing detected");  ASSERT_DEATH(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());}#endif  // _WIN32TEST(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.  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 *counter)      : counter_(counter) {}  virtual ~DestructorCountingCostFunction() {    *counter_ += 1;  }  virtual bool Evaluate(double const* const* parameters,                        double* residuals,                        double** jacobians) const {    return true;  } private:  int* counter_;};TEST(Problem, ReusedCostFunctionsAreOnlyDeletedOnce) {  double y[4], z[5];  int counter = 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(&counter);    problem.AddResidualBlock(cost, NULL, y, z);    problem.AddResidualBlock(cost, NULL, y, z);    problem.AddResidualBlock(cost, NULL, y, z);  }  // Check that the destructor was called only once.  CHECK_EQ(counter, 1);}}  // namespace internal}  // namespace ceres
 |