Home | History | Annotate | Download | only in ceres
      1 // Ceres Solver - A fast non-linear least squares minimizer
      2 // Copyright 2013 Google Inc. All rights reserved.
      3 // http://code.google.com/p/ceres-solver/
      4 //
      5 // Redistribution and use in source and binary forms, with or without
      6 // modification, are permitted provided that the following conditions are met:
      7 //
      8 // * Redistributions of source code must retain the above copyright notice,
      9 //   this list of conditions and the following disclaimer.
     10 // * Redistributions in binary form must reproduce the above copyright notice,
     11 //   this list of conditions and the following disclaimer in the documentation
     12 //   and/or other materials provided with the distribution.
     13 // * Neither the name of Google Inc. nor the names of its contributors may be
     14 //   used to endorse or promote products derived from this software without
     15 //   specific prior written permission.
     16 //
     17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     18 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     19 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     20 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
     21 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     22 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     23 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     24 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     25 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     26 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     27 // POSSIBILITY OF SUCH DAMAGE.
     28 //
     29 // Author: sameeragarwal (at) google.com (Sameer Agarwal)
     30 //         mierle (at) gmail.com (Keir Mierle)
     31 
     32 #include <cstddef>
     33 
     34 #include "ceres/dynamic_numeric_diff_cost_function.h"
     35 #include "ceres/internal/scoped_ptr.h"
     36 #include "gtest/gtest.h"
     37 
     38 namespace ceres {
     39 namespace internal {
     40 
     41 const double kTolerance = 1e-6;
     42 
     43 // Takes 2 parameter blocks:
     44 //     parameters[0] is size 10.
     45 //     parameters[1] is size 5.
     46 // Emits 21 residuals:
     47 //     A: i - parameters[0][i], for i in [0,10)  -- this is 10 residuals
     48 //     B: parameters[0][i] - i, for i in [0,10)  -- this is another 10.
     49 //     C: sum(parameters[0][i]^2 - 8*parameters[0][i]) + sum(parameters[1][i])
     50 class MyCostFunctor {
     51  public:
     52   bool operator()(double const* const* parameters, double* residuals) const {
     53     const double* params0 = parameters[0];
     54     int r = 0;
     55     for (int i = 0; i < 10; ++i) {
     56       residuals[r++] = i - params0[i];
     57       residuals[r++] = params0[i] - i;
     58     }
     59 
     60     double c_residual = 0.0;
     61     for (int i = 0; i < 10; ++i) {
     62       c_residual += pow(params0[i], 2) - 8.0 * params0[i];
     63     }
     64 
     65     const double* params1 = parameters[1];
     66     for (int i = 0; i < 5; ++i) {
     67       c_residual += params1[i];
     68     }
     69     residuals[r++] = c_residual;
     70     return true;
     71   }
     72 };
     73 
     74 TEST(DynamicNumericdiffCostFunctionTest, TestResiduals) {
     75   vector<double> param_block_0(10, 0.0);
     76   vector<double> param_block_1(5, 0.0);
     77   DynamicNumericDiffCostFunction<MyCostFunctor> cost_function(
     78       new MyCostFunctor());
     79   cost_function.AddParameterBlock(param_block_0.size());
     80   cost_function.AddParameterBlock(param_block_1.size());
     81   cost_function.SetNumResiduals(21);
     82 
     83   // Test residual computation.
     84   vector<double> residuals(21, -100000);
     85   vector<double*> parameter_blocks(2);
     86   parameter_blocks[0] = &param_block_0[0];
     87   parameter_blocks[1] = &param_block_1[0];
     88   EXPECT_TRUE(cost_function.Evaluate(&parameter_blocks[0],
     89                                      residuals.data(),
     90                                      NULL));
     91   for (int r = 0; r < 10; ++r) {
     92     EXPECT_EQ(1.0 * r, residuals.at(r * 2));
     93     EXPECT_EQ(-1.0 * r, residuals.at(r * 2 + 1));
     94   }
     95   EXPECT_EQ(0, residuals.at(20));
     96 }
     97 
     98 
     99 TEST(DynamicNumericdiffCostFunctionTest, TestJacobian) {
    100   // Test the residual counting.
    101   vector<double> param_block_0(10, 0.0);
    102   for (int i = 0; i < 10; ++i) {
    103     param_block_0[i] = 2 * i;
    104   }
    105   vector<double> param_block_1(5, 0.0);
    106   DynamicNumericDiffCostFunction<MyCostFunctor> cost_function(
    107       new MyCostFunctor());
    108   cost_function.AddParameterBlock(param_block_0.size());
    109   cost_function.AddParameterBlock(param_block_1.size());
    110   cost_function.SetNumResiduals(21);
    111 
    112   // Prepare the residuals.
    113   vector<double> residuals(21, -100000);
    114 
    115   // Prepare the parameters.
    116   vector<double*> parameter_blocks(2);
    117   parameter_blocks[0] = &param_block_0[0];
    118   parameter_blocks[1] = &param_block_1[0];
    119 
    120   // Prepare the jacobian.
    121   vector<vector<double> > jacobian_vect(2);
    122   jacobian_vect[0].resize(21 * 10, -100000);
    123   jacobian_vect[1].resize(21 * 5, -100000);
    124   vector<double*> jacobian;
    125   jacobian.push_back(jacobian_vect[0].data());
    126   jacobian.push_back(jacobian_vect[1].data());
    127 
    128   // Test jacobian computation.
    129   EXPECT_TRUE(cost_function.Evaluate(parameter_blocks.data(),
    130                                      residuals.data(),
    131                                      jacobian.data()));
    132 
    133   for (int r = 0; r < 10; ++r) {
    134     EXPECT_EQ(-1.0 * r, residuals.at(r * 2));
    135     EXPECT_EQ(+1.0 * r, residuals.at(r * 2 + 1));
    136   }
    137   EXPECT_EQ(420, residuals.at(20));
    138   for (int p = 0; p < 10; ++p) {
    139     // Check "A" Jacobian.
    140     EXPECT_NEAR(-1.0, jacobian_vect[0][2*p * 10 + p], kTolerance);
    141     // Check "B" Jacobian.
    142     EXPECT_NEAR(+1.0, jacobian_vect[0][(2*p+1) * 10 + p], kTolerance);
    143     jacobian_vect[0][2*p * 10 + p] = 0.0;
    144     jacobian_vect[0][(2*p+1) * 10 + p] = 0.0;
    145   }
    146 
    147   // Check "C" Jacobian for first parameter block.
    148   for (int p = 0; p < 10; ++p) {
    149     EXPECT_NEAR(4 * p - 8, jacobian_vect[0][20 * 10 + p], kTolerance);
    150     jacobian_vect[0][20 * 10 + p] = 0.0;
    151   }
    152   for (int i = 0; i < jacobian_vect[0].size(); ++i) {
    153     EXPECT_NEAR(0.0, jacobian_vect[0][i], kTolerance);
    154   }
    155 
    156   // Check "C" Jacobian for second parameter block.
    157   for (int p = 0; p < 5; ++p) {
    158     EXPECT_NEAR(1.0, jacobian_vect[1][20 * 5 + p], kTolerance);
    159     jacobian_vect[1][20 * 5 + p] = 0.0;
    160   }
    161   for (int i = 0; i < jacobian_vect[1].size(); ++i) {
    162     EXPECT_NEAR(0.0, jacobian_vect[1][i], kTolerance);
    163   }
    164 }
    165 
    166 TEST(DynamicNumericdiffCostFunctionTest, JacobianWithFirstParameterBlockConstant) {  // NOLINT
    167   // Test the residual counting.
    168   vector<double> param_block_0(10, 0.0);
    169   for (int i = 0; i < 10; ++i) {
    170     param_block_0[i] = 2 * i;
    171   }
    172   vector<double> param_block_1(5, 0.0);
    173   DynamicNumericDiffCostFunction<MyCostFunctor> cost_function(
    174       new MyCostFunctor());
    175   cost_function.AddParameterBlock(param_block_0.size());
    176   cost_function.AddParameterBlock(param_block_1.size());
    177   cost_function.SetNumResiduals(21);
    178 
    179   // Prepare the residuals.
    180   vector<double> residuals(21, -100000);
    181 
    182   // Prepare the parameters.
    183   vector<double*> parameter_blocks(2);
    184   parameter_blocks[0] = &param_block_0[0];
    185   parameter_blocks[1] = &param_block_1[0];
    186 
    187   // Prepare the jacobian.
    188   vector<vector<double> > jacobian_vect(2);
    189   jacobian_vect[0].resize(21 * 10, -100000);
    190   jacobian_vect[1].resize(21 * 5, -100000);
    191   vector<double*> jacobian;
    192   jacobian.push_back(NULL);
    193   jacobian.push_back(jacobian_vect[1].data());
    194 
    195   // Test jacobian computation.
    196   EXPECT_TRUE(cost_function.Evaluate(parameter_blocks.data(),
    197                                      residuals.data(),
    198                                      jacobian.data()));
    199 
    200   for (int r = 0; r < 10; ++r) {
    201     EXPECT_EQ(-1.0 * r, residuals.at(r * 2));
    202     EXPECT_EQ(+1.0 * r, residuals.at(r * 2 + 1));
    203   }
    204   EXPECT_EQ(420, residuals.at(20));
    205 
    206   // Check "C" Jacobian for second parameter block.
    207   for (int p = 0; p < 5; ++p) {
    208     EXPECT_NEAR(1.0, jacobian_vect[1][20 * 5 + p], kTolerance);
    209     jacobian_vect[1][20 * 5 + p] = 0.0;
    210   }
    211   for (int i = 0; i < jacobian_vect[1].size(); ++i) {
    212     EXPECT_EQ(0.0, jacobian_vect[1][i]);
    213   }
    214 }
    215 
    216 TEST(DynamicNumericdiffCostFunctionTest, JacobianWithSecondParameterBlockConstant) {  // NOLINT
    217   // Test the residual counting.
    218   vector<double> param_block_0(10, 0.0);
    219   for (int i = 0; i < 10; ++i) {
    220     param_block_0[i] = 2 * i;
    221   }
    222   vector<double> param_block_1(5, 0.0);
    223   DynamicNumericDiffCostFunction<MyCostFunctor> cost_function(
    224       new MyCostFunctor());
    225   cost_function.AddParameterBlock(param_block_0.size());
    226   cost_function.AddParameterBlock(param_block_1.size());
    227   cost_function.SetNumResiduals(21);
    228 
    229   // Prepare the residuals.
    230   vector<double> residuals(21, -100000);
    231 
    232   // Prepare the parameters.
    233   vector<double*> parameter_blocks(2);
    234   parameter_blocks[0] = &param_block_0[0];
    235   parameter_blocks[1] = &param_block_1[0];
    236 
    237   // Prepare the jacobian.
    238   vector<vector<double> > jacobian_vect(2);
    239   jacobian_vect[0].resize(21 * 10, -100000);
    240   jacobian_vect[1].resize(21 * 5, -100000);
    241   vector<double*> jacobian;
    242   jacobian.push_back(jacobian_vect[0].data());
    243   jacobian.push_back(NULL);
    244 
    245   // Test jacobian computation.
    246   EXPECT_TRUE(cost_function.Evaluate(parameter_blocks.data(),
    247                                      residuals.data(),
    248                                      jacobian.data()));
    249 
    250   for (int r = 0; r < 10; ++r) {
    251     EXPECT_EQ(-1.0 * r, residuals.at(r * 2));
    252     EXPECT_EQ(+1.0 * r, residuals.at(r * 2 + 1));
    253   }
    254   EXPECT_EQ(420, residuals.at(20));
    255   for (int p = 0; p < 10; ++p) {
    256     // Check "A" Jacobian.
    257     EXPECT_NEAR(-1.0, jacobian_vect[0][2*p * 10 + p], kTolerance);
    258     // Check "B" Jacobian.
    259     EXPECT_NEAR(+1.0, jacobian_vect[0][(2*p+1) * 10 + p], kTolerance);
    260     jacobian_vect[0][2*p * 10 + p] = 0.0;
    261     jacobian_vect[0][(2*p+1) * 10 + p] = 0.0;
    262   }
    263 
    264   // Check "C" Jacobian for first parameter block.
    265   for (int p = 0; p < 10; ++p) {
    266     EXPECT_NEAR(4 * p - 8, jacobian_vect[0][20 * 10 + p], kTolerance);
    267     jacobian_vect[0][20 * 10 + p] = 0.0;
    268   }
    269   for (int i = 0; i < jacobian_vect[0].size(); ++i) {
    270     EXPECT_EQ(0.0, jacobian_vect[0][i]);
    271   }
    272 }
    273 
    274 // Takes 3 parameter blocks:
    275 //     parameters[0] (x) is size 1.
    276 //     parameters[1] (y) is size 2.
    277 //     parameters[2] (z) is size 3.
    278 // Emits 7 residuals:
    279 //     A: x[0] (= sum_x)
    280 //     B: y[0] + 2.0 * y[1] (= sum_y)
    281 //     C: z[0] + 3.0 * z[1] + 6.0 * z[2] (= sum_z)
    282 //     D: sum_x * sum_y
    283 //     E: sum_y * sum_z
    284 //     F: sum_x * sum_z
    285 //     G: sum_x * sum_y * sum_z
    286 class MyThreeParameterCostFunctor {
    287  public:
    288   template <typename T>
    289   bool operator()(T const* const* parameters, T* residuals) const {
    290     const T* x = parameters[0];
    291     const T* y = parameters[1];
    292     const T* z = parameters[2];
    293 
    294     T sum_x = x[0];
    295     T sum_y = y[0] + 2.0 * y[1];
    296     T sum_z = z[0] + 3.0 * z[1] + 6.0 * z[2];
    297 
    298     residuals[0] = sum_x;
    299     residuals[1] = sum_y;
    300     residuals[2] = sum_z;
    301     residuals[3] = sum_x * sum_y;
    302     residuals[4] = sum_y * sum_z;
    303     residuals[5] = sum_x * sum_z;
    304     residuals[6] = sum_x * sum_y * sum_z;
    305     return true;
    306   }
    307 };
    308 
    309 class ThreeParameterCostFunctorTest : public ::testing::Test {
    310  protected:
    311   virtual void SetUp() {
    312     // Prepare the parameters.
    313     x_.resize(1);
    314     x_[0] = 0.0;
    315 
    316     y_.resize(2);
    317     y_[0] = 1.0;
    318     y_[1] = 3.0;
    319 
    320     z_.resize(3);
    321     z_[0] = 2.0;
    322     z_[1] = 4.0;
    323     z_[2] = 6.0;
    324 
    325     parameter_blocks_.resize(3);
    326     parameter_blocks_[0] = &x_[0];
    327     parameter_blocks_[1] = &y_[0];
    328     parameter_blocks_[2] = &z_[0];
    329 
    330     // Prepare the cost function.
    331     typedef DynamicNumericDiffCostFunction<MyThreeParameterCostFunctor>
    332       DynamicMyThreeParameterCostFunction;
    333     DynamicMyThreeParameterCostFunction * cost_function =
    334       new DynamicMyThreeParameterCostFunction(
    335         new MyThreeParameterCostFunctor());
    336     cost_function->AddParameterBlock(1);
    337     cost_function->AddParameterBlock(2);
    338     cost_function->AddParameterBlock(3);
    339     cost_function->SetNumResiduals(7);
    340 
    341     cost_function_.reset(cost_function);
    342 
    343     // Setup jacobian data.
    344     jacobian_vect_.resize(3);
    345     jacobian_vect_[0].resize(7 * x_.size(), -100000);
    346     jacobian_vect_[1].resize(7 * y_.size(), -100000);
    347     jacobian_vect_[2].resize(7 * z_.size(), -100000);
    348 
    349     // Prepare the expected residuals.
    350     const double sum_x = x_[0];
    351     const double sum_y = y_[0] + 2.0 * y_[1];
    352     const double sum_z = z_[0] + 3.0 * z_[1] + 6.0 * z_[2];
    353 
    354     expected_residuals_.resize(7);
    355     expected_residuals_[0] = sum_x;
    356     expected_residuals_[1] = sum_y;
    357     expected_residuals_[2] = sum_z;
    358     expected_residuals_[3] = sum_x * sum_y;
    359     expected_residuals_[4] = sum_y * sum_z;
    360     expected_residuals_[5] = sum_x * sum_z;
    361     expected_residuals_[6] = sum_x * sum_y * sum_z;
    362 
    363     // Prepare the expected jacobian entries.
    364     expected_jacobian_x_.resize(7);
    365     expected_jacobian_x_[0] = 1.0;
    366     expected_jacobian_x_[1] = 0.0;
    367     expected_jacobian_x_[2] = 0.0;
    368     expected_jacobian_x_[3] = sum_y;
    369     expected_jacobian_x_[4] = 0.0;
    370     expected_jacobian_x_[5] = sum_z;
    371     expected_jacobian_x_[6] = sum_y * sum_z;
    372 
    373     expected_jacobian_y_.resize(14);
    374     expected_jacobian_y_[0] = 0.0;
    375     expected_jacobian_y_[1] = 0.0;
    376     expected_jacobian_y_[2] = 1.0;
    377     expected_jacobian_y_[3] = 2.0;
    378     expected_jacobian_y_[4] = 0.0;
    379     expected_jacobian_y_[5] = 0.0;
    380     expected_jacobian_y_[6] = sum_x;
    381     expected_jacobian_y_[7] = 2.0 * sum_x;
    382     expected_jacobian_y_[8] = sum_z;
    383     expected_jacobian_y_[9] = 2.0 * sum_z;
    384     expected_jacobian_y_[10] = 0.0;
    385     expected_jacobian_y_[11] = 0.0;
    386     expected_jacobian_y_[12] = sum_x * sum_z;
    387     expected_jacobian_y_[13] = 2.0 * sum_x * sum_z;
    388 
    389     expected_jacobian_z_.resize(21);
    390     expected_jacobian_z_[0] = 0.0;
    391     expected_jacobian_z_[1] = 0.0;
    392     expected_jacobian_z_[2] = 0.0;
    393     expected_jacobian_z_[3] = 0.0;
    394     expected_jacobian_z_[4] = 0.0;
    395     expected_jacobian_z_[5] = 0.0;
    396     expected_jacobian_z_[6] = 1.0;
    397     expected_jacobian_z_[7] = 3.0;
    398     expected_jacobian_z_[8] = 6.0;
    399     expected_jacobian_z_[9] = 0.0;
    400     expected_jacobian_z_[10] = 0.0;
    401     expected_jacobian_z_[11] = 0.0;
    402     expected_jacobian_z_[12] = sum_y;
    403     expected_jacobian_z_[13] = 3.0 * sum_y;
    404     expected_jacobian_z_[14] = 6.0 * sum_y;
    405     expected_jacobian_z_[15] = sum_x;
    406     expected_jacobian_z_[16] = 3.0 * sum_x;
    407     expected_jacobian_z_[17] = 6.0 * sum_x;
    408     expected_jacobian_z_[18] = sum_x * sum_y;
    409     expected_jacobian_z_[19] = 3.0 * sum_x * sum_y;
    410     expected_jacobian_z_[20] = 6.0 * sum_x * sum_y;
    411   }
    412 
    413  protected:
    414   vector<double> x_;
    415   vector<double> y_;
    416   vector<double> z_;
    417 
    418   vector<double*> parameter_blocks_;
    419 
    420   scoped_ptr<CostFunction> cost_function_;
    421 
    422   vector<vector<double> > jacobian_vect_;
    423 
    424   vector<double> expected_residuals_;
    425 
    426   vector<double> expected_jacobian_x_;
    427   vector<double> expected_jacobian_y_;
    428   vector<double> expected_jacobian_z_;
    429 };
    430 
    431 TEST_F(ThreeParameterCostFunctorTest, TestThreeParameterResiduals) {
    432   vector<double> residuals(7, -100000);
    433   EXPECT_TRUE(cost_function_->Evaluate(parameter_blocks_.data(),
    434                                        residuals.data(),
    435                                        NULL));
    436   for (int i = 0; i < 7; ++i) {
    437     EXPECT_EQ(expected_residuals_[i], residuals[i]);
    438   }
    439 }
    440 
    441 TEST_F(ThreeParameterCostFunctorTest, TestThreeParameterJacobian) {
    442   vector<double> residuals(7, -100000);
    443 
    444   vector<double*> jacobian;
    445   jacobian.push_back(jacobian_vect_[0].data());
    446   jacobian.push_back(jacobian_vect_[1].data());
    447   jacobian.push_back(jacobian_vect_[2].data());
    448 
    449   EXPECT_TRUE(cost_function_->Evaluate(parameter_blocks_.data(),
    450                                        residuals.data(),
    451                                        jacobian.data()));
    452 
    453   for (int i = 0; i < 7; ++i) {
    454     EXPECT_EQ(expected_residuals_[i], residuals[i]);
    455   }
    456 
    457   for (int i = 0; i < 7; ++i) {
    458     EXPECT_NEAR(expected_jacobian_x_[i], jacobian[0][i], kTolerance);
    459   }
    460 
    461   for (int i = 0; i < 14; ++i) {
    462     EXPECT_NEAR(expected_jacobian_y_[i], jacobian[1][i], kTolerance);
    463   }
    464 
    465   for (int i = 0; i < 21; ++i) {
    466     EXPECT_NEAR(expected_jacobian_z_[i], jacobian[2][i], kTolerance);
    467   }
    468 }
    469 
    470 TEST_F(ThreeParameterCostFunctorTest,
    471        ThreeParameterJacobianWithFirstAndLastParameterBlockConstant) {
    472   vector<double> residuals(7, -100000);
    473 
    474   vector<double*> jacobian;
    475   jacobian.push_back(NULL);
    476   jacobian.push_back(jacobian_vect_[1].data());
    477   jacobian.push_back(NULL);
    478 
    479   EXPECT_TRUE(cost_function_->Evaluate(parameter_blocks_.data(),
    480                                        residuals.data(),
    481                                        jacobian.data()));
    482 
    483   for (int i = 0; i < 7; ++i) {
    484     EXPECT_EQ(expected_residuals_[i], residuals[i]);
    485   }
    486 
    487   for (int i = 0; i < 14; ++i) {
    488     EXPECT_NEAR(expected_jacobian_y_[i], jacobian[1][i], kTolerance);
    489   }
    490 }
    491 
    492 TEST_F(ThreeParameterCostFunctorTest,
    493        ThreeParameterJacobianWithSecondParameterBlockConstant) {
    494   vector<double> residuals(7, -100000);
    495 
    496   vector<double*> jacobian;
    497   jacobian.push_back(jacobian_vect_[0].data());
    498   jacobian.push_back(NULL);
    499   jacobian.push_back(jacobian_vect_[2].data());
    500 
    501   EXPECT_TRUE(cost_function_->Evaluate(parameter_blocks_.data(),
    502                                        residuals.data(),
    503                                        jacobian.data()));
    504 
    505   for (int i = 0; i < 7; ++i) {
    506     EXPECT_EQ(expected_residuals_[i], residuals[i]);
    507   }
    508 
    509   for (int i = 0; i < 7; ++i) {
    510     EXPECT_NEAR(expected_jacobian_x_[i], jacobian[0][i], kTolerance);
    511   }
    512 
    513   for (int i = 0; i < 21; ++i) {
    514     EXPECT_NEAR(expected_jacobian_z_[i], jacobian[2][i], kTolerance);
    515   }
    516 }
    517 
    518 }  // namespace internal
    519 }  // namespace ceres
    520