Home | History | Annotate | Download | only in ceres
      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: keir (at) google.com (Keir Mierle)
     30 //
     31 // Tests shared across evaluators. The tests try all combinations of linear
     32 // solver and num_eliminate_blocks (for schur-based solvers).
     33 
     34 #include "ceres/evaluator.h"
     35 
     36 #include "ceres/casts.h"
     37 #include "ceres/cost_function.h"
     38 #include "ceres/crs_matrix.h"
     39 #include "ceres/evaluator_test_utils.h"
     40 #include "ceres/internal/eigen.h"
     41 #include "ceres/internal/scoped_ptr.h"
     42 #include "ceres/local_parameterization.h"
     43 #include "ceres/problem_impl.h"
     44 #include "ceres/program.h"
     45 #include "ceres/sized_cost_function.h"
     46 #include "ceres/sparse_matrix.h"
     47 #include "ceres/stringprintf.h"
     48 #include "ceres/types.h"
     49 #include "gtest/gtest.h"
     50 
     51 namespace ceres {
     52 namespace internal {
     53 
     54 // TODO(keir): Consider pushing this into a common test utils file.
     55 template<int kFactor, int kNumResiduals,
     56          int N0 = 0, int N1 = 0, int N2 = 0, bool kSucceeds = true>
     57 class ParameterIgnoringCostFunction
     58     : public SizedCostFunction<kNumResiduals, N0, N1, N2> {
     59   typedef SizedCostFunction<kNumResiduals, N0, N1, N2> Base;
     60  public:
     61   virtual bool Evaluate(double const* const* parameters,
     62                         double* residuals,
     63                         double** jacobians) const {
     64     for (int i = 0; i < Base::num_residuals(); ++i) {
     65       residuals[i] = i + 1;
     66     }
     67     if (jacobians) {
     68       for (int k = 0; k < Base::parameter_block_sizes().size(); ++k) {
     69         // The jacobians here are full sized, but they are transformed in the
     70         // evaluator into the "local" jacobian. In the tests, the "subset
     71         // constant" parameterization is used, which should pick out columns
     72         // from these jacobians. Put values in the jacobian that make this
     73         // obvious; in particular, make the jacobians like this:
     74         //
     75         //   1 2 3 4 ...
     76         //   1 2 3 4 ...   .*  kFactor
     77         //   1 2 3 4 ...
     78         //
     79         // where the multiplication by kFactor makes it easier to distinguish
     80         // between Jacobians of different residuals for the same parameter.
     81         if (jacobians[k] != NULL) {
     82           MatrixRef jacobian(jacobians[k],
     83                              Base::num_residuals(),
     84                              Base::parameter_block_sizes()[k]);
     85           for (int j = 0; j < Base::parameter_block_sizes()[k]; ++j) {
     86             jacobian.col(j).setConstant(kFactor * (j + 1));
     87           }
     88         }
     89       }
     90     }
     91     return kSucceeds;
     92   }
     93 };
     94 
     95 struct EvaluatorTestOptions {
     96   EvaluatorTestOptions(LinearSolverType linear_solver_type,
     97                        int num_eliminate_blocks,
     98                        bool dynamic_sparsity = false)
     99     : linear_solver_type(linear_solver_type),
    100       num_eliminate_blocks(num_eliminate_blocks),
    101       dynamic_sparsity(dynamic_sparsity) {}
    102 
    103   LinearSolverType linear_solver_type;
    104   int num_eliminate_blocks;
    105   bool dynamic_sparsity;
    106 };
    107 
    108 struct EvaluatorTest
    109     : public ::testing::TestWithParam<EvaluatorTestOptions> {
    110   Evaluator* CreateEvaluator(Program* program) {
    111     // This program is straight from the ProblemImpl, and so has no index/offset
    112     // yet; compute it here as required by the evalutor implementations.
    113     program->SetParameterOffsetsAndIndex();
    114 
    115     if (VLOG_IS_ON(1)) {
    116       string report;
    117       StringAppendF(&report, "Creating evaluator with type: %d",
    118                     GetParam().linear_solver_type);
    119       if (GetParam().linear_solver_type == SPARSE_NORMAL_CHOLESKY) {
    120         StringAppendF(&report, ", dynamic_sparsity: %d",
    121                       GetParam().dynamic_sparsity);
    122       }
    123       StringAppendF(&report, " and num_eliminate_blocks: %d",
    124                     GetParam().num_eliminate_blocks);
    125       VLOG(1) << report;
    126     }
    127     Evaluator::Options options;
    128     options.linear_solver_type = GetParam().linear_solver_type;
    129     options.num_eliminate_blocks = GetParam().num_eliminate_blocks;
    130     options.dynamic_sparsity = GetParam().dynamic_sparsity;
    131     string error;
    132     return Evaluator::Create(options, program, &error);
    133   }
    134 
    135   void EvaluateAndCompare(ProblemImpl *problem,
    136                           int expected_num_rows,
    137                           int expected_num_cols,
    138                           double expected_cost,
    139                           const double* expected_residuals,
    140                           const double* expected_gradient,
    141                           const double* expected_jacobian) {
    142     scoped_ptr<Evaluator> evaluator(
    143         CreateEvaluator(problem->mutable_program()));
    144     int num_residuals = expected_num_rows;
    145     int num_parameters = expected_num_cols;
    146 
    147     double cost = -1;
    148 
    149     Vector residuals(num_residuals);
    150     residuals.setConstant(-2000);
    151 
    152     Vector gradient(num_parameters);
    153     gradient.setConstant(-3000);
    154 
    155     scoped_ptr<SparseMatrix> jacobian(evaluator->CreateJacobian());
    156 
    157     ASSERT_EQ(expected_num_rows, evaluator->NumResiduals());
    158     ASSERT_EQ(expected_num_cols, evaluator->NumEffectiveParameters());
    159     ASSERT_EQ(expected_num_rows, jacobian->num_rows());
    160     ASSERT_EQ(expected_num_cols, jacobian->num_cols());
    161 
    162     vector<double> state(evaluator->NumParameters());
    163 
    164     ASSERT_TRUE(evaluator->Evaluate(
    165           &state[0],
    166           &cost,
    167           expected_residuals != NULL ? &residuals[0]  : NULL,
    168           expected_gradient  != NULL ? &gradient[0]   : NULL,
    169           expected_jacobian  != NULL ? jacobian.get() : NULL));
    170 
    171     Matrix actual_jacobian;
    172     if (expected_jacobian != NULL) {
    173       jacobian->ToDenseMatrix(&actual_jacobian);
    174     }
    175 
    176     CompareEvaluations(expected_num_rows,
    177                        expected_num_cols,
    178                        expected_cost,
    179                        expected_residuals,
    180                        expected_gradient,
    181                        expected_jacobian,
    182                        cost,
    183                        &residuals[0],
    184                        &gradient[0],
    185                        actual_jacobian.data());
    186   }
    187 
    188   // Try all combinations of parameters for the evaluator.
    189   void CheckAllEvaluationCombinations(const ExpectedEvaluation &expected) {
    190     for (int i = 0; i < 8; ++i) {
    191       EvaluateAndCompare(&problem,
    192                          expected.num_rows,
    193                          expected.num_cols,
    194                          expected.cost,
    195                          (i & 1) ? expected.residuals : NULL,
    196                          (i & 2) ? expected.gradient  : NULL,
    197                          (i & 4) ? expected.jacobian  : NULL);
    198     }
    199   }
    200 
    201   // The values are ignored completely by the cost function.
    202   double x[2];
    203   double y[3];
    204   double z[4];
    205 
    206   ProblemImpl problem;
    207 };
    208 
    209 void SetSparseMatrixConstant(SparseMatrix* sparse_matrix, double value) {
    210   VectorRef(sparse_matrix->mutable_values(),
    211             sparse_matrix->num_nonzeros()).setConstant(value);
    212 }
    213 
    214 TEST_P(EvaluatorTest, SingleResidualProblem) {
    215   problem.AddResidualBlock(new ParameterIgnoringCostFunction<1, 3, 2, 3, 4>,
    216                            NULL,
    217                            x, y, z);
    218 
    219   ExpectedEvaluation expected = {
    220     // Rows/columns
    221     3, 9,
    222     // Cost
    223     7.0,
    224     // Residuals
    225     { 1.0, 2.0, 3.0 },
    226     // Gradient
    227     { 6.0, 12.0,              // x
    228       6.0, 12.0, 18.0,        // y
    229       6.0, 12.0, 18.0, 24.0,  // z
    230     },
    231     // Jacobian
    232     //   x          y             z
    233     { 1, 2,   1, 2, 3,   1, 2, 3, 4,
    234       1, 2,   1, 2, 3,   1, 2, 3, 4,
    235       1, 2,   1, 2, 3,   1, 2, 3, 4
    236     }
    237   };
    238   CheckAllEvaluationCombinations(expected);
    239 }
    240 
    241 TEST_P(EvaluatorTest, SingleResidualProblemWithPermutedParameters) {
    242   // Add the parameters in explicit order to force the ordering in the program.
    243   problem.AddParameterBlock(x,  2);
    244   problem.AddParameterBlock(y,  3);
    245   problem.AddParameterBlock(z,  4);
    246 
    247   // Then use a cost function which is similar to the others, but swap around
    248   // the ordering of the parameters to the cost function. This shouldn't affect
    249   // the jacobian evaluation, but requires explicit handling in the evaluators.
    250   // At one point the compressed row evaluator had a bug that went undetected
    251   // for a long time, since by chance most users added parameters to the problem
    252   // in the same order that they occured as parameters to a cost function.
    253   problem.AddResidualBlock(new ParameterIgnoringCostFunction<1, 3, 4, 3, 2>,
    254                            NULL,
    255                            z, y, x);
    256 
    257   ExpectedEvaluation expected = {
    258     // Rows/columns
    259     3, 9,
    260     // Cost
    261     7.0,
    262     // Residuals
    263     { 1.0, 2.0, 3.0 },
    264     // Gradient
    265     { 6.0, 12.0,              // x
    266       6.0, 12.0, 18.0,        // y
    267       6.0, 12.0, 18.0, 24.0,  // z
    268     },
    269     // Jacobian
    270     //   x          y             z
    271     { 1, 2,   1, 2, 3,   1, 2, 3, 4,
    272       1, 2,   1, 2, 3,   1, 2, 3, 4,
    273       1, 2,   1, 2, 3,   1, 2, 3, 4
    274     }
    275   };
    276   CheckAllEvaluationCombinations(expected);
    277 }
    278 
    279 TEST_P(EvaluatorTest, SingleResidualProblemWithNuisanceParameters) {
    280   // These parameters are not used.
    281   double a[2];
    282   double b[1];
    283   double c[1];
    284   double d[3];
    285 
    286   // Add the parameters in a mixed order so the Jacobian is "checkered" with the
    287   // values from the other parameters.
    288   problem.AddParameterBlock(a, 2);
    289   problem.AddParameterBlock(x, 2);
    290   problem.AddParameterBlock(b, 1);
    291   problem.AddParameterBlock(y, 3);
    292   problem.AddParameterBlock(c, 1);
    293   problem.AddParameterBlock(z, 4);
    294   problem.AddParameterBlock(d, 3);
    295 
    296   problem.AddResidualBlock(new ParameterIgnoringCostFunction<1, 3, 2, 3, 4>,
    297                            NULL,
    298                            x, y, z);
    299 
    300   ExpectedEvaluation expected = {
    301     // Rows/columns
    302     3, 16,
    303     // Cost
    304     7.0,
    305     // Residuals
    306     { 1.0, 2.0, 3.0 },
    307     // Gradient
    308     { 0.0, 0.0,               // a
    309       6.0, 12.0,              // x
    310       0.0,                    // b
    311       6.0, 12.0, 18.0,        // y
    312       0.0,                    // c
    313       6.0, 12.0, 18.0, 24.0,  // z
    314       0.0, 0.0, 0.0,          // d
    315     },
    316     // Jacobian
    317     //   a        x     b           y     c              z           d
    318     { 0, 0,    1, 2,    0,    1, 2, 3,    0,    1, 2, 3, 4,    0, 0, 0,
    319       0, 0,    1, 2,    0,    1, 2, 3,    0,    1, 2, 3, 4,    0, 0, 0,
    320       0, 0,    1, 2,    0,    1, 2, 3,    0,    1, 2, 3, 4,    0, 0, 0
    321     }
    322   };
    323   CheckAllEvaluationCombinations(expected);
    324 }
    325 
    326 TEST_P(EvaluatorTest, MultipleResidualProblem) {
    327   // Add the parameters in explicit order to force the ordering in the program.
    328   problem.AddParameterBlock(x,  2);
    329   problem.AddParameterBlock(y,  3);
    330   problem.AddParameterBlock(z,  4);
    331 
    332   // f(x, y) in R^2
    333   problem.AddResidualBlock(new ParameterIgnoringCostFunction<1, 2, 2, 3>,
    334                            NULL,
    335                            x, y);
    336 
    337   // g(x, z) in R^3
    338   problem.AddResidualBlock(new ParameterIgnoringCostFunction<2, 3, 2, 4>,
    339                            NULL,
    340                            x, z);
    341 
    342   // h(y, z) in R^4
    343   problem.AddResidualBlock(new ParameterIgnoringCostFunction<3, 4, 3, 4>,
    344                            NULL,
    345                            y, z);
    346 
    347   ExpectedEvaluation expected = {
    348     // Rows/columns
    349     9, 9,
    350     // Cost
    351     // f       g           h
    352     (  1 + 4 + 1 + 4 + 9 + 1 + 4 + 9 + 16) / 2.0,
    353     // Residuals
    354     { 1.0, 2.0,           // f
    355       1.0, 2.0, 3.0,      // g
    356       1.0, 2.0, 3.0, 4.0  // h
    357     },
    358     // Gradient
    359     { 15.0, 30.0,               // x
    360       33.0, 66.0, 99.0,         // y
    361       42.0, 84.0, 126.0, 168.0  // z
    362     },
    363     // Jacobian
    364     //                x        y           z
    365     {   /* f(x, y) */ 1, 2,    1, 2, 3,    0, 0, 0, 0,
    366                       1, 2,    1, 2, 3,    0, 0, 0, 0,
    367 
    368         /* g(x, z) */ 2, 4,    0, 0, 0,    2, 4, 6, 8,
    369                       2, 4,    0, 0, 0,    2, 4, 6, 8,
    370                       2, 4,    0, 0, 0,    2, 4, 6, 8,
    371 
    372         /* h(y, z) */ 0, 0,    3, 6, 9,    3, 6, 9, 12,
    373                       0, 0,    3, 6, 9,    3, 6, 9, 12,
    374                       0, 0,    3, 6, 9,    3, 6, 9, 12,
    375                       0, 0,    3, 6, 9,    3, 6, 9, 12
    376     }
    377   };
    378   CheckAllEvaluationCombinations(expected);
    379 }
    380 
    381 TEST_P(EvaluatorTest, MultipleResidualsWithLocalParameterizations) {
    382   // Add the parameters in explicit order to force the ordering in the program.
    383   problem.AddParameterBlock(x,  2);
    384 
    385   // Fix y's first dimension.
    386   vector<int> y_fixed;
    387   y_fixed.push_back(0);
    388   problem.AddParameterBlock(y, 3, new SubsetParameterization(3, y_fixed));
    389 
    390   // Fix z's second dimension.
    391   vector<int> z_fixed;
    392   z_fixed.push_back(1);
    393   problem.AddParameterBlock(z, 4, new SubsetParameterization(4, z_fixed));
    394 
    395   // f(x, y) in R^2
    396   problem.AddResidualBlock(new ParameterIgnoringCostFunction<1, 2, 2, 3>,
    397                            NULL,
    398                            x, y);
    399 
    400   // g(x, z) in R^3
    401   problem.AddResidualBlock(new ParameterIgnoringCostFunction<2, 3, 2, 4>,
    402                            NULL,
    403                            x, z);
    404 
    405   // h(y, z) in R^4
    406   problem.AddResidualBlock(new ParameterIgnoringCostFunction<3, 4, 3, 4>,
    407                            NULL,
    408                            y, z);
    409 
    410   ExpectedEvaluation expected = {
    411     // Rows/columns
    412     9, 7,
    413     // Cost
    414     // f       g           h
    415     (  1 + 4 + 1 + 4 + 9 + 1 + 4 + 9 + 16) / 2.0,
    416     // Residuals
    417     { 1.0, 2.0,           // f
    418       1.0, 2.0, 3.0,      // g
    419       1.0, 2.0, 3.0, 4.0  // h
    420     },
    421     // Gradient
    422     { 15.0, 30.0,         // x
    423       66.0, 99.0,         // y
    424       42.0, 126.0, 168.0  // z
    425     },
    426     // Jacobian
    427     //                x        y           z
    428     {   /* f(x, y) */ 1, 2,    2, 3,    0, 0, 0,
    429                       1, 2,    2, 3,    0, 0, 0,
    430 
    431         /* g(x, z) */ 2, 4,    0, 0,    2, 6, 8,
    432                       2, 4,    0, 0,    2, 6, 8,
    433                       2, 4,    0, 0,    2, 6, 8,
    434 
    435         /* h(y, z) */ 0, 0,    6, 9,    3, 9, 12,
    436                       0, 0,    6, 9,    3, 9, 12,
    437                       0, 0,    6, 9,    3, 9, 12,
    438                       0, 0,    6, 9,    3, 9, 12
    439     }
    440   };
    441   CheckAllEvaluationCombinations(expected);
    442 }
    443 
    444 TEST_P(EvaluatorTest, MultipleResidualProblemWithSomeConstantParameters) {
    445   // The values are ignored completely by the cost function.
    446   double x[2];
    447   double y[3];
    448   double z[4];
    449 
    450   // Add the parameters in explicit order to force the ordering in the program.
    451   problem.AddParameterBlock(x,  2);
    452   problem.AddParameterBlock(y,  3);
    453   problem.AddParameterBlock(z,  4);
    454 
    455   // f(x, y) in R^2
    456   problem.AddResidualBlock(new ParameterIgnoringCostFunction<1, 2, 2, 3>,
    457                            NULL,
    458                            x, y);
    459 
    460   // g(x, z) in R^3
    461   problem.AddResidualBlock(new ParameterIgnoringCostFunction<2, 3, 2, 4>,
    462                            NULL,
    463                            x, z);
    464 
    465   // h(y, z) in R^4
    466   problem.AddResidualBlock(new ParameterIgnoringCostFunction<3, 4, 3, 4>,
    467                            NULL,
    468                            y, z);
    469 
    470   // For this test, "z" is constant.
    471   problem.SetParameterBlockConstant(z);
    472 
    473   // Create the reduced program which is missing the fixed "z" variable.
    474   // Normally, the preprocessing of the program that happens in solver_impl
    475   // takes care of this, but we don't want to invoke the solver here.
    476   Program reduced_program;
    477   vector<ParameterBlock*>* parameter_blocks =
    478       problem.mutable_program()->mutable_parameter_blocks();
    479 
    480   // "z" is the last parameter; save it for later and pop it off temporarily.
    481   // Note that "z" will still get read during evaluation, so it cannot be
    482   // deleted at this point.
    483   ParameterBlock* parameter_block_z = parameter_blocks->back();
    484   parameter_blocks->pop_back();
    485 
    486   ExpectedEvaluation expected = {
    487     // Rows/columns
    488     9, 5,
    489     // Cost
    490     // f       g           h
    491     (  1 + 4 + 1 + 4 + 9 + 1 + 4 + 9 + 16) / 2.0,
    492     // Residuals
    493     { 1.0, 2.0,           // f
    494       1.0, 2.0, 3.0,      // g
    495       1.0, 2.0, 3.0, 4.0  // h
    496     },
    497     // Gradient
    498     { 15.0, 30.0,        // x
    499       33.0, 66.0, 99.0,  // y
    500     },
    501     // Jacobian
    502     //                x        y
    503     {   /* f(x, y) */ 1, 2,    1, 2, 3,
    504                       1, 2,    1, 2, 3,
    505 
    506         /* g(x, z) */ 2, 4,    0, 0, 0,
    507                       2, 4,    0, 0, 0,
    508                       2, 4,    0, 0, 0,
    509 
    510         /* h(y, z) */ 0, 0,    3, 6, 9,
    511                       0, 0,    3, 6, 9,
    512                       0, 0,    3, 6, 9,
    513                       0, 0,    3, 6, 9
    514     }
    515   };
    516   CheckAllEvaluationCombinations(expected);
    517 
    518   // Restore parameter block z, so it will get freed in a consistent way.
    519   parameter_blocks->push_back(parameter_block_z);
    520 }
    521 
    522 TEST_P(EvaluatorTest, EvaluatorAbortsForResidualsThatFailToEvaluate) {
    523   // Switch the return value to failure.
    524   problem.AddResidualBlock(
    525       new ParameterIgnoringCostFunction<20, 3, 2, 3, 4, false>, NULL, x, y, z);
    526 
    527   // The values are ignored.
    528   double state[9];
    529 
    530   scoped_ptr<Evaluator> evaluator(CreateEvaluator(problem.mutable_program()));
    531   scoped_ptr<SparseMatrix> jacobian(evaluator->CreateJacobian());
    532   double cost;
    533   EXPECT_FALSE(evaluator->Evaluate(state, &cost, NULL, NULL, NULL));
    534 }
    535 
    536 // In the pairs, the first argument is the linear solver type, and the second
    537 // argument is num_eliminate_blocks. Changing the num_eliminate_blocks only
    538 // makes sense for the schur-based solvers.
    539 //
    540 // Try all values of num_eliminate_blocks that make sense given that in the
    541 // tests a maximum of 4 parameter blocks are present.
    542 INSTANTIATE_TEST_CASE_P(
    543     LinearSolvers,
    544     EvaluatorTest,
    545     ::testing::Values(
    546       EvaluatorTestOptions(DENSE_QR, 0),
    547       EvaluatorTestOptions(DENSE_SCHUR, 0),
    548       EvaluatorTestOptions(DENSE_SCHUR, 1),
    549       EvaluatorTestOptions(DENSE_SCHUR, 2),
    550       EvaluatorTestOptions(DENSE_SCHUR, 3),
    551       EvaluatorTestOptions(DENSE_SCHUR, 4),
    552       EvaluatorTestOptions(SPARSE_SCHUR, 0),
    553       EvaluatorTestOptions(SPARSE_SCHUR, 1),
    554       EvaluatorTestOptions(SPARSE_SCHUR, 2),
    555       EvaluatorTestOptions(SPARSE_SCHUR, 3),
    556       EvaluatorTestOptions(SPARSE_SCHUR, 4),
    557       EvaluatorTestOptions(ITERATIVE_SCHUR, 0),
    558       EvaluatorTestOptions(ITERATIVE_SCHUR, 1),
    559       EvaluatorTestOptions(ITERATIVE_SCHUR, 2),
    560       EvaluatorTestOptions(ITERATIVE_SCHUR, 3),
    561       EvaluatorTestOptions(ITERATIVE_SCHUR, 4),
    562       EvaluatorTestOptions(SPARSE_NORMAL_CHOLESKY, 0, false),
    563       EvaluatorTestOptions(SPARSE_NORMAL_CHOLESKY, 0, true)));
    564 
    565 // Simple cost function used to check if the evaluator is sensitive to
    566 // state changes.
    567 class ParameterSensitiveCostFunction : public SizedCostFunction<2, 2> {
    568  public:
    569   virtual bool Evaluate(double const* const* parameters,
    570                         double* residuals,
    571                         double** jacobians) const {
    572     double x1 = parameters[0][0];
    573     double x2 = parameters[0][1];
    574     residuals[0] = x1 * x1;
    575     residuals[1] = x2 * x2;
    576 
    577     if (jacobians != NULL) {
    578       double* jacobian = jacobians[0];
    579       if (jacobian != NULL) {
    580         jacobian[0] = 2.0 * x1;
    581         jacobian[1] = 0.0;
    582         jacobian[2] = 0.0;
    583         jacobian[3] = 2.0 * x2;
    584       }
    585     }
    586     return true;
    587   }
    588 };
    589 
    590 TEST(Evaluator, EvaluatorRespectsParameterChanges) {
    591   ProblemImpl problem;
    592 
    593   double x[2];
    594   x[0] = 1.0;
    595   x[1] = 1.0;
    596 
    597   problem.AddResidualBlock(new ParameterSensitiveCostFunction(), NULL, x);
    598   Program* program = problem.mutable_program();
    599   program->SetParameterOffsetsAndIndex();
    600 
    601   Evaluator::Options options;
    602   options.linear_solver_type = DENSE_QR;
    603   options.num_eliminate_blocks = 0;
    604   string error;
    605   scoped_ptr<Evaluator> evaluator(Evaluator::Create(options, program, &error));
    606   scoped_ptr<SparseMatrix> jacobian(evaluator->CreateJacobian());
    607 
    608   ASSERT_EQ(2, jacobian->num_rows());
    609   ASSERT_EQ(2, jacobian->num_cols());
    610 
    611   double state[2];
    612   state[0] = 2.0;
    613   state[1] = 3.0;
    614 
    615   // The original state of a residual block comes from the user's
    616   // state. So the original state is 1.0, 1.0, and the only way we get
    617   // the 2.0, 3.0 results in the following tests is if it respects the
    618   // values in the state vector.
    619 
    620   // Cost only; no residuals and no jacobian.
    621   {
    622     double cost = -1;
    623     ASSERT_TRUE(evaluator->Evaluate(state, &cost, NULL, NULL, NULL));
    624     EXPECT_EQ(48.5, cost);
    625   }
    626 
    627   // Cost and residuals, no jacobian.
    628   {
    629     double cost = -1;
    630     double residuals[2] = { -2, -2 };
    631     ASSERT_TRUE(evaluator->Evaluate(state, &cost, residuals, NULL, NULL));
    632     EXPECT_EQ(48.5, cost);
    633     EXPECT_EQ(4, residuals[0]);
    634     EXPECT_EQ(9, residuals[1]);
    635   }
    636 
    637   // Cost, residuals, and jacobian.
    638   {
    639     double cost = -1;
    640     double residuals[2] = { -2, -2};
    641     SetSparseMatrixConstant(jacobian.get(), -1);
    642     ASSERT_TRUE(evaluator->Evaluate(state,
    643                                     &cost,
    644                                     residuals,
    645                                     NULL,
    646                                     jacobian.get()));
    647     EXPECT_EQ(48.5, cost);
    648     EXPECT_EQ(4, residuals[0]);
    649     EXPECT_EQ(9, residuals[1]);
    650     Matrix actual_jacobian;
    651     jacobian->ToDenseMatrix(&actual_jacobian);
    652 
    653     Matrix expected_jacobian(2, 2);
    654     expected_jacobian
    655         << 2 * state[0], 0,
    656            0, 2 * state[1];
    657 
    658     EXPECT_TRUE((actual_jacobian.array() == expected_jacobian.array()).all())
    659         << "Actual:\n" << actual_jacobian
    660         << "\nExpected:\n" << expected_jacobian;
    661   }
    662 }
    663 
    664 }  // namespace internal
    665 }  // namespace ceres
    666