|  | // Ceres Solver - A fast non-linear least squares minimizer | 
|  | // Copyright 2010, 2011, 2012 Google Inc. All rights reserved. | 
|  | // http://code.google.com/p/ceres-solver/ | 
|  | // | 
|  | // Redistribution and use in source and binary forms, with or without | 
|  | // modification, are permitted provided that the following conditions are met: | 
|  | // | 
|  | // * Redistributions of source code must retain the above copyright notice, | 
|  | //   this list of conditions and the following disclaimer. | 
|  | // * Redistributions in binary form must reproduce the above copyright notice, | 
|  | //   this list of conditions and the following disclaimer in the documentation | 
|  | //   and/or other materials provided with the distribution. | 
|  | // * Neither the name of Google Inc. nor the names of its contributors may be | 
|  | //   used to endorse or promote products derived from this software without | 
|  | //   specific prior written permission. | 
|  | // | 
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | 
|  | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 
|  | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | 
|  | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | 
|  | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | 
|  | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | 
|  | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | 
|  | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | 
|  | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | 
|  | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | 
|  | // POSSIBILITY OF SUCH DAMAGE. | 
|  | // | 
|  | // Author: sameeragarwal@google.com (Sameer Agarwal) | 
|  | //         keir@google.com (Keir Mierle) | 
|  |  | 
|  | #include "ceres/problem.h" | 
|  | #include "ceres/problem_impl.h" | 
|  |  | 
|  | #include "ceres/casts.h" | 
|  | #include "ceres/cost_function.h" | 
|  | #include "ceres/crs_matrix.h" | 
|  | #include "ceres/evaluator_test_utils.cc" | 
|  | #include "ceres/internal/eigen.h" | 
|  | #include "ceres/internal/scoped_ptr.h" | 
|  | #include "ceres/local_parameterization.h" | 
|  | #include "ceres/map_util.h" | 
|  | #include "ceres/parameter_block.h" | 
|  | #include "ceres/program.h" | 
|  | #include "ceres/sized_cost_function.h" | 
|  | #include "ceres/sparse_matrix.h" | 
|  | #include "ceres/types.h" | 
|  | #include "gtest/gtest.h" | 
|  |  | 
|  | namespace ceres { | 
|  | namespace internal { | 
|  |  | 
|  | // The following three classes are for the purposes of defining | 
|  | // function signatures. They have dummy Evaluate functions. | 
|  |  | 
|  | // Trivial cost function that accepts a single argument. | 
|  | class UnaryCostFunction : public CostFunction { | 
|  | public: | 
|  | UnaryCostFunction(int num_residuals, int16 parameter_block_size) { | 
|  | set_num_residuals(num_residuals); | 
|  | mutable_parameter_block_sizes()->push_back(parameter_block_size); | 
|  | } | 
|  | virtual ~UnaryCostFunction() {} | 
|  |  | 
|  | virtual bool Evaluate(double const* const* parameters, | 
|  | double* residuals, | 
|  | double** jacobians) const { | 
|  | for (int i = 0; i < num_residuals(); ++i) { | 
|  | residuals[i] = 1; | 
|  | } | 
|  | return true; | 
|  | } | 
|  | }; | 
|  |  | 
|  | // Trivial cost function that accepts two arguments. | 
|  | class BinaryCostFunction: public CostFunction { | 
|  | public: | 
|  | BinaryCostFunction(int num_residuals, | 
|  | int16 parameter_block1_size, | 
|  | int16 parameter_block2_size) { | 
|  | set_num_residuals(num_residuals); | 
|  | mutable_parameter_block_sizes()->push_back(parameter_block1_size); | 
|  | mutable_parameter_block_sizes()->push_back(parameter_block2_size); | 
|  | } | 
|  |  | 
|  | virtual bool Evaluate(double const* const* parameters, | 
|  | double* residuals, | 
|  | double** jacobians) const { | 
|  | for (int i = 0; i < num_residuals(); ++i) { | 
|  | residuals[i] = 2; | 
|  | } | 
|  | return true; | 
|  | } | 
|  | }; | 
|  |  | 
|  | // Trivial cost function that accepts three arguments. | 
|  | class TernaryCostFunction: public CostFunction { | 
|  | public: | 
|  | TernaryCostFunction(int num_residuals, | 
|  | int16 parameter_block1_size, | 
|  | int16 parameter_block2_size, | 
|  | int16 parameter_block3_size) { | 
|  | set_num_residuals(num_residuals); | 
|  | mutable_parameter_block_sizes()->push_back(parameter_block1_size); | 
|  | mutable_parameter_block_sizes()->push_back(parameter_block2_size); | 
|  | mutable_parameter_block_sizes()->push_back(parameter_block3_size); | 
|  | } | 
|  |  | 
|  | virtual bool Evaluate(double const* const* parameters, | 
|  | double* residuals, | 
|  | double** jacobians) const { | 
|  | for (int i = 0; i < num_residuals(); ++i) { | 
|  | residuals[i] = 3; | 
|  | } | 
|  | return true; | 
|  | } | 
|  | }; | 
|  |  | 
|  | TEST(Problem, AddResidualWithNullCostFunctionDies) { | 
|  | double x[3], y[4], z[5]; | 
|  |  | 
|  | Problem problem; | 
|  | problem.AddParameterBlock(x, 3); | 
|  | problem.AddParameterBlock(y, 4); | 
|  | problem.AddParameterBlock(z, 5); | 
|  |  | 
|  | EXPECT_DEATH_IF_SUPPORTED(problem.AddResidualBlock(NULL, NULL, x), | 
|  | "'cost_function' Must be non NULL"); | 
|  | } | 
|  |  | 
|  | TEST(Problem, AddResidualWithIncorrectNumberOfParameterBlocksDies) { | 
|  | double x[3], y[4], z[5]; | 
|  |  | 
|  | Problem problem; | 
|  | problem.AddParameterBlock(x, 3); | 
|  | problem.AddParameterBlock(y, 4); | 
|  | problem.AddParameterBlock(z, 5); | 
|  |  | 
|  | // UnaryCostFunction takes only one parameter, but two are passed. | 
|  | EXPECT_DEATH_IF_SUPPORTED( | 
|  | problem.AddResidualBlock(new UnaryCostFunction(2, 3), NULL, x, y), | 
|  | "parameter_blocks.size()"); | 
|  | } | 
|  |  | 
|  | TEST(Problem, AddResidualWithDifferentSizesOnTheSameVariableDies) { | 
|  | double x[3]; | 
|  |  | 
|  | Problem problem; | 
|  | problem.AddResidualBlock(new UnaryCostFunction(2, 3), NULL, x); | 
|  | EXPECT_DEATH_IF_SUPPORTED(problem.AddResidualBlock( | 
|  | new UnaryCostFunction( | 
|  | 2, 4 /* 4 != 3 */), NULL, x), | 
|  | "different block sizes"); | 
|  | } | 
|  |  | 
|  | TEST(Problem, AddResidualWithDuplicateParametersDies) { | 
|  | double x[3], z[5]; | 
|  |  | 
|  | Problem problem; | 
|  | EXPECT_DEATH_IF_SUPPORTED(problem.AddResidualBlock( | 
|  | new BinaryCostFunction(2, 3, 3), NULL, x, x), | 
|  | "Duplicate parameter blocks"); | 
|  | EXPECT_DEATH_IF_SUPPORTED(problem.AddResidualBlock( | 
|  | new TernaryCostFunction(1, 5, 3, 5), | 
|  | NULL, z, x, z), | 
|  | "Duplicate parameter blocks"); | 
|  | } | 
|  |  | 
|  | TEST(Problem, AddResidualWithIncorrectSizesOfParameterBlockDies) { | 
|  | double x[3], y[4], z[5]; | 
|  |  | 
|  | Problem problem; | 
|  | problem.AddParameterBlock(x, 3); | 
|  | problem.AddParameterBlock(y, 4); | 
|  | problem.AddParameterBlock(z, 5); | 
|  |  | 
|  | // The cost function expects the size of the second parameter, z, to be 4 | 
|  | // instead of 5 as declared above. This is fatal. | 
|  | EXPECT_DEATH_IF_SUPPORTED(problem.AddResidualBlock( | 
|  | new BinaryCostFunction(2, 3, 4), NULL, x, z), | 
|  | "different block sizes"); | 
|  | } | 
|  |  | 
|  | TEST(Problem, AddResidualAddsDuplicatedParametersOnlyOnce) { | 
|  | double x[3], y[4], z[5]; | 
|  |  | 
|  | Problem problem; | 
|  | problem.AddResidualBlock(new UnaryCostFunction(2, 3), NULL, x); | 
|  | problem.AddResidualBlock(new UnaryCostFunction(2, 3), NULL, x); | 
|  | problem.AddResidualBlock(new UnaryCostFunction(2, 4), NULL, y); | 
|  | problem.AddResidualBlock(new UnaryCostFunction(2, 5), NULL, z); | 
|  |  | 
|  | EXPECT_EQ(3, problem.NumParameterBlocks()); | 
|  | EXPECT_EQ(12, problem.NumParameters()); | 
|  | } | 
|  |  | 
|  | TEST(Problem, AddParameterWithDifferentSizesOnTheSameVariableDies) { | 
|  | double x[3], y[4]; | 
|  |  | 
|  | Problem problem; | 
|  | problem.AddParameterBlock(x, 3); | 
|  | problem.AddParameterBlock(y, 4); | 
|  |  | 
|  | EXPECT_DEATH_IF_SUPPORTED(problem.AddParameterBlock(x, 4), | 
|  | "different block sizes"); | 
|  | } | 
|  |  | 
|  | static double *IntToPtr(int i) { | 
|  | return reinterpret_cast<double*>(sizeof(double) * i);  // NOLINT | 
|  | } | 
|  |  | 
|  | TEST(Problem, AddParameterWithAliasedParametersDies) { | 
|  | // Layout is | 
|  | // | 
|  | //   0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 | 
|  | //                 [x] x  x  x  x          [y] y  y | 
|  | //         o==o==o                 o==o==o           o==o | 
|  | //               o--o--o     o--o--o     o--o  o--o--o | 
|  | // | 
|  | // Parameter block additions are tested as listed above; expected successful | 
|  | // ones marked with o==o and aliasing ones marked with o--o. | 
|  |  | 
|  | Problem problem; | 
|  | problem.AddParameterBlock(IntToPtr(5),  5);  // x | 
|  | problem.AddParameterBlock(IntToPtr(13), 3);  // y | 
|  |  | 
|  | EXPECT_DEATH_IF_SUPPORTED(problem.AddParameterBlock(IntToPtr( 4), 2), | 
|  | "Aliasing detected"); | 
|  | EXPECT_DEATH_IF_SUPPORTED(problem.AddParameterBlock(IntToPtr( 4), 3), | 
|  | "Aliasing detected"); | 
|  | EXPECT_DEATH_IF_SUPPORTED(problem.AddParameterBlock(IntToPtr( 4), 9), | 
|  | "Aliasing detected"); | 
|  | EXPECT_DEATH_IF_SUPPORTED(problem.AddParameterBlock(IntToPtr( 8), 3), | 
|  | "Aliasing detected"); | 
|  | EXPECT_DEATH_IF_SUPPORTED(problem.AddParameterBlock(IntToPtr(12), 2), | 
|  | "Aliasing detected"); | 
|  | EXPECT_DEATH_IF_SUPPORTED(problem.AddParameterBlock(IntToPtr(14), 3), | 
|  | "Aliasing detected"); | 
|  |  | 
|  | // These ones should work. | 
|  | problem.AddParameterBlock(IntToPtr( 2), 3); | 
|  | problem.AddParameterBlock(IntToPtr(10), 3); | 
|  | problem.AddParameterBlock(IntToPtr(16), 2); | 
|  |  | 
|  | ASSERT_EQ(5, problem.NumParameterBlocks()); | 
|  | } | 
|  |  | 
|  | TEST(Problem, AddParameterIgnoresDuplicateCalls) { | 
|  | double x[3], y[4]; | 
|  |  | 
|  | Problem problem; | 
|  | problem.AddParameterBlock(x, 3); | 
|  | problem.AddParameterBlock(y, 4); | 
|  |  | 
|  | // Creating parameter blocks multiple times is ignored. | 
|  | problem.AddParameterBlock(x, 3); | 
|  | problem.AddResidualBlock(new UnaryCostFunction(2, 3), NULL, x); | 
|  |  | 
|  | // ... even repeatedly. | 
|  | problem.AddParameterBlock(x, 3); | 
|  | problem.AddResidualBlock(new UnaryCostFunction(2, 3), NULL, x); | 
|  |  | 
|  | // More parameters are fine. | 
|  | problem.AddParameterBlock(y, 4); | 
|  | problem.AddResidualBlock(new UnaryCostFunction(2, 4), NULL, y); | 
|  |  | 
|  | EXPECT_EQ(2, problem.NumParameterBlocks()); | 
|  | EXPECT_EQ(7, problem.NumParameters()); | 
|  | } | 
|  |  | 
|  | TEST(Problem, AddingParametersAndResidualsResultsInExpectedProblem) { | 
|  | double x[3], y[4], z[5], w[4]; | 
|  |  | 
|  | Problem problem; | 
|  | problem.AddParameterBlock(x, 3); | 
|  | EXPECT_EQ(1, problem.NumParameterBlocks()); | 
|  | EXPECT_EQ(3, problem.NumParameters()); | 
|  |  | 
|  | problem.AddParameterBlock(y, 4); | 
|  | EXPECT_EQ(2, problem.NumParameterBlocks()); | 
|  | EXPECT_EQ(7, problem.NumParameters()); | 
|  |  | 
|  | problem.AddParameterBlock(z, 5); | 
|  | EXPECT_EQ(3,  problem.NumParameterBlocks()); | 
|  | EXPECT_EQ(12, problem.NumParameters()); | 
|  |  | 
|  | // Add a parameter that has a local parameterization. | 
|  | w[0] = 1.0; w[1] = 0.0; w[2] = 0.0; w[3] = 0.0; | 
|  | problem.AddParameterBlock(w, 4, new QuaternionParameterization); | 
|  | EXPECT_EQ(4,  problem.NumParameterBlocks()); | 
|  | EXPECT_EQ(16, problem.NumParameters()); | 
|  |  | 
|  | problem.AddResidualBlock(new UnaryCostFunction(2, 3), NULL, x); | 
|  | problem.AddResidualBlock(new BinaryCostFunction(6, 5, 4) , NULL, z, y); | 
|  | problem.AddResidualBlock(new BinaryCostFunction(3, 3, 5), NULL, x, z); | 
|  | problem.AddResidualBlock(new BinaryCostFunction(7, 5, 3), NULL, z, x); | 
|  | problem.AddResidualBlock(new TernaryCostFunction(1, 5, 3, 4), NULL, z, x, y); | 
|  |  | 
|  | const int total_residuals = 2 + 6 + 3 + 7 + 1; | 
|  | EXPECT_EQ(problem.NumResidualBlocks(), 5); | 
|  | EXPECT_EQ(problem.NumResiduals(), total_residuals); | 
|  | } | 
|  |  | 
|  | class DestructorCountingCostFunction : public SizedCostFunction<3, 4, 5> { | 
|  | public: | 
|  | explicit DestructorCountingCostFunction(int *num_destructions) | 
|  | : num_destructions_(num_destructions) {} | 
|  |  | 
|  | virtual ~DestructorCountingCostFunction() { | 
|  | *num_destructions_ += 1; | 
|  | } | 
|  |  | 
|  | virtual bool Evaluate(double const* const* parameters, | 
|  | double* residuals, | 
|  | double** jacobians) const { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | private: | 
|  | int* num_destructions_; | 
|  | }; | 
|  |  | 
|  | TEST(Problem, ReusedCostFunctionsAreOnlyDeletedOnce) { | 
|  | double y[4], z[5]; | 
|  | int num_destructions = 0; | 
|  |  | 
|  | // Add a cost function multiple times and check to make sure that | 
|  | // the destructor on the cost function is only called once. | 
|  | { | 
|  | Problem problem; | 
|  | problem.AddParameterBlock(y, 4); | 
|  | problem.AddParameterBlock(z, 5); | 
|  |  | 
|  | CostFunction* cost = new DestructorCountingCostFunction(&num_destructions); | 
|  | problem.AddResidualBlock(cost, NULL, y, z); | 
|  | problem.AddResidualBlock(cost, NULL, y, z); | 
|  | problem.AddResidualBlock(cost, NULL, y, z); | 
|  | EXPECT_EQ(3, problem.NumResidualBlocks()); | 
|  | } | 
|  |  | 
|  | // Check that the destructor was called only once. | 
|  | CHECK_EQ(num_destructions, 1); | 
|  | } | 
|  |  | 
|  | TEST(Problem, CostFunctionsAreDeletedEvenWithRemovals) { | 
|  | double y[4], z[5], w[4]; | 
|  | int num_destructions = 0; | 
|  | { | 
|  | Problem problem; | 
|  | problem.AddParameterBlock(y, 4); | 
|  | problem.AddParameterBlock(z, 5); | 
|  |  | 
|  | CostFunction* cost_yz = | 
|  | new DestructorCountingCostFunction(&num_destructions); | 
|  | CostFunction* cost_wz = | 
|  | new DestructorCountingCostFunction(&num_destructions); | 
|  | ResidualBlock* r_yz = problem.AddResidualBlock(cost_yz, NULL, y, z); | 
|  | ResidualBlock* r_wz = problem.AddResidualBlock(cost_wz, NULL, w, z); | 
|  | EXPECT_EQ(2, problem.NumResidualBlocks()); | 
|  |  | 
|  | // In the current implementation, the destructor shouldn't get run yet. | 
|  | problem.RemoveResidualBlock(r_yz); | 
|  | CHECK_EQ(num_destructions, 0); | 
|  | problem.RemoveResidualBlock(r_wz); | 
|  | CHECK_EQ(num_destructions, 0); | 
|  |  | 
|  | EXPECT_EQ(0, problem.NumResidualBlocks()); | 
|  | } | 
|  | CHECK_EQ(num_destructions, 2); | 
|  | } | 
|  |  | 
|  | // Make the dynamic problem tests (e.g. for removing residual blocks) | 
|  | // parameterized on whether the low-latency mode is enabled or not. | 
|  | // | 
|  | // This tests against ProblemImpl instead of Problem in order to inspect the | 
|  | // state of the resulting Program; this is difficult with only the thin Problem | 
|  | // interface. | 
|  | struct DynamicProblem : public ::testing::TestWithParam<bool> { | 
|  | DynamicProblem() { | 
|  | Problem::Options options; | 
|  | options.enable_fast_parameter_block_removal = GetParam(); | 
|  | problem.reset(new ProblemImpl(options)); | 
|  | } | 
|  |  | 
|  | ParameterBlock* GetParameterBlock(int block) { | 
|  | return problem->program().parameter_blocks()[block]; | 
|  | } | 
|  | ResidualBlock* GetResidualBlock(int block) { | 
|  | return problem->program().residual_blocks()[block]; | 
|  | } | 
|  |  | 
|  | bool HasResidualBlock(ResidualBlock* residual_block) { | 
|  | return find(problem->program().residual_blocks().begin(), | 
|  | problem->program().residual_blocks().end(), | 
|  | residual_block) != problem->program().residual_blocks().end(); | 
|  | } | 
|  |  | 
|  | // The next block of functions until the end are only for testing the | 
|  | // residual block removals. | 
|  | void ExpectParameterBlockContainsResidualBlock( | 
|  | double* values, | 
|  | ResidualBlock* residual_block) { | 
|  | ParameterBlock* parameter_block = | 
|  | FindOrDie(problem->parameter_map(), values); | 
|  | EXPECT_TRUE(ContainsKey(*(parameter_block->mutable_residual_blocks()), | 
|  | residual_block)); | 
|  | } | 
|  |  | 
|  | void ExpectSize(double* values, int size) { | 
|  | ParameterBlock* parameter_block = | 
|  | FindOrDie(problem->parameter_map(), values); | 
|  | EXPECT_EQ(size, parameter_block->mutable_residual_blocks()->size()); | 
|  | } | 
|  |  | 
|  | // Degenerate case. | 
|  | void ExpectParameterBlockContains(double* values) { | 
|  | ExpectSize(values, 0); | 
|  | } | 
|  |  | 
|  | void ExpectParameterBlockContains(double* values, | 
|  | ResidualBlock* r1) { | 
|  | ExpectSize(values, 1); | 
|  | ExpectParameterBlockContainsResidualBlock(values, r1); | 
|  | } | 
|  |  | 
|  | void ExpectParameterBlockContains(double* values, | 
|  | ResidualBlock* r1, | 
|  | ResidualBlock* r2) { | 
|  | ExpectSize(values, 2); | 
|  | ExpectParameterBlockContainsResidualBlock(values, r1); | 
|  | ExpectParameterBlockContainsResidualBlock(values, r2); | 
|  | } | 
|  |  | 
|  | void ExpectParameterBlockContains(double* values, | 
|  | ResidualBlock* r1, | 
|  | ResidualBlock* r2, | 
|  | ResidualBlock* r3) { | 
|  | ExpectSize(values, 3); | 
|  | ExpectParameterBlockContainsResidualBlock(values, r1); | 
|  | ExpectParameterBlockContainsResidualBlock(values, r2); | 
|  | ExpectParameterBlockContainsResidualBlock(values, r3); | 
|  | } | 
|  |  | 
|  | void ExpectParameterBlockContains(double* values, | 
|  | ResidualBlock* r1, | 
|  | ResidualBlock* r2, | 
|  | ResidualBlock* r3, | 
|  | ResidualBlock* r4) { | 
|  | ExpectSize(values, 4); | 
|  | ExpectParameterBlockContainsResidualBlock(values, r1); | 
|  | ExpectParameterBlockContainsResidualBlock(values, r2); | 
|  | ExpectParameterBlockContainsResidualBlock(values, r3); | 
|  | ExpectParameterBlockContainsResidualBlock(values, r4); | 
|  | } | 
|  |  | 
|  | scoped_ptr<ProblemImpl> problem; | 
|  | double y[4], z[5], w[3]; | 
|  | }; | 
|  |  | 
|  | TEST(Problem, SetParameterBlockConstantWithUnknownPtrDies) { | 
|  | double x[3]; | 
|  | double y[2]; | 
|  |  | 
|  | Problem problem; | 
|  | problem.AddParameterBlock(x, 3); | 
|  |  | 
|  | EXPECT_DEATH_IF_SUPPORTED(problem.SetParameterBlockConstant(y), | 
|  | "Parameter block not found:"); | 
|  | } | 
|  |  | 
|  | TEST(Problem, SetParameterBlockVariableWithUnknownPtrDies) { | 
|  | double x[3]; | 
|  | double y[2]; | 
|  |  | 
|  | Problem problem; | 
|  | problem.AddParameterBlock(x, 3); | 
|  |  | 
|  | EXPECT_DEATH_IF_SUPPORTED(problem.SetParameterBlockVariable(y), | 
|  | "Parameter block not found:"); | 
|  | } | 
|  |  | 
|  | TEST(Problem, SetLocalParameterizationWithUnknownPtrDies) { | 
|  | double x[3]; | 
|  | double y[2]; | 
|  |  | 
|  | Problem problem; | 
|  | problem.AddParameterBlock(x, 3); | 
|  |  | 
|  | EXPECT_DEATH_IF_SUPPORTED( | 
|  | problem.SetParameterization(y, new IdentityParameterization(3)), | 
|  | "Parameter block not found:"); | 
|  | } | 
|  |  | 
|  | TEST(Problem, RemoveParameterBlockWithUnknownPtrDies) { | 
|  | double x[3]; | 
|  | double y[2]; | 
|  |  | 
|  | Problem problem; | 
|  | problem.AddParameterBlock(x, 3); | 
|  |  | 
|  | EXPECT_DEATH_IF_SUPPORTED( | 
|  | problem.RemoveParameterBlock(y), "Parameter block not found:"); | 
|  | } | 
|  |  | 
|  | TEST(Problem, ParameterBlockQueryTest) { | 
|  | double x[3]; | 
|  | double y[4]; | 
|  | Problem problem; | 
|  | problem.AddParameterBlock(x, 3); | 
|  | problem.AddParameterBlock(y, 4); | 
|  |  | 
|  | vector<int> constant_parameters; | 
|  | constant_parameters.push_back(0); | 
|  | problem.SetParameterization( | 
|  | x, | 
|  | new SubsetParameterization(3, constant_parameters)); | 
|  | EXPECT_EQ(problem.ParameterBlockSize(x), 3); | 
|  | EXPECT_EQ(problem.ParameterBlockLocalSize(x), 2); | 
|  | EXPECT_EQ(problem.ParameterBlockLocalSize(y), 4); | 
|  |  | 
|  | vector<double*> parameter_blocks; | 
|  | problem.GetParameterBlocks(¶meter_blocks); | 
|  | EXPECT_EQ(parameter_blocks.size(), 2); | 
|  | EXPECT_NE(parameter_blocks[0], parameter_blocks[1]); | 
|  | EXPECT_TRUE(parameter_blocks[0] == x || parameter_blocks[0] == y); | 
|  | EXPECT_TRUE(parameter_blocks[1] == x || parameter_blocks[1] == y); | 
|  |  | 
|  | problem.RemoveParameterBlock(x); | 
|  | problem.GetParameterBlocks(¶meter_blocks); | 
|  | EXPECT_EQ(parameter_blocks.size(), 1); | 
|  | EXPECT_TRUE(parameter_blocks[0] == y); | 
|  | } | 
|  |  | 
|  | TEST_P(DynamicProblem, RemoveParameterBlockWithNoResiduals) { | 
|  | problem->AddParameterBlock(y, 4); | 
|  | problem->AddParameterBlock(z, 5); | 
|  | problem->AddParameterBlock(w, 3); | 
|  | ASSERT_EQ(3, problem->NumParameterBlocks()); | 
|  | ASSERT_EQ(0, problem->NumResidualBlocks()); | 
|  | EXPECT_EQ(y, GetParameterBlock(0)->user_state()); | 
|  | EXPECT_EQ(z, GetParameterBlock(1)->user_state()); | 
|  | EXPECT_EQ(w, GetParameterBlock(2)->user_state()); | 
|  |  | 
|  | // w is at the end, which might break the swapping logic so try adding and | 
|  | // removing it. | 
|  | problem->RemoveParameterBlock(w); | 
|  | ASSERT_EQ(2, problem->NumParameterBlocks()); | 
|  | ASSERT_EQ(0, problem->NumResidualBlocks()); | 
|  | EXPECT_EQ(y, GetParameterBlock(0)->user_state()); | 
|  | EXPECT_EQ(z, GetParameterBlock(1)->user_state()); | 
|  | problem->AddParameterBlock(w, 3); | 
|  | ASSERT_EQ(3, problem->NumParameterBlocks()); | 
|  | ASSERT_EQ(0, problem->NumResidualBlocks()); | 
|  | EXPECT_EQ(y, GetParameterBlock(0)->user_state()); | 
|  | EXPECT_EQ(z, GetParameterBlock(1)->user_state()); | 
|  | EXPECT_EQ(w, GetParameterBlock(2)->user_state()); | 
|  |  | 
|  | // Now remove z, which is in the middle, and add it back. | 
|  | problem->RemoveParameterBlock(z); | 
|  | ASSERT_EQ(2, problem->NumParameterBlocks()); | 
|  | ASSERT_EQ(0, problem->NumResidualBlocks()); | 
|  | EXPECT_EQ(y, GetParameterBlock(0)->user_state()); | 
|  | EXPECT_EQ(w, GetParameterBlock(1)->user_state()); | 
|  | problem->AddParameterBlock(z, 5); | 
|  | ASSERT_EQ(3, problem->NumParameterBlocks()); | 
|  | ASSERT_EQ(0, problem->NumResidualBlocks()); | 
|  | EXPECT_EQ(y, GetParameterBlock(0)->user_state()); | 
|  | EXPECT_EQ(w, GetParameterBlock(1)->user_state()); | 
|  | EXPECT_EQ(z, GetParameterBlock(2)->user_state()); | 
|  |  | 
|  | // Now remove everything. | 
|  | // y | 
|  | problem->RemoveParameterBlock(y); | 
|  | ASSERT_EQ(2, problem->NumParameterBlocks()); | 
|  | ASSERT_EQ(0, problem->NumResidualBlocks()); | 
|  | EXPECT_EQ(z, GetParameterBlock(0)->user_state()); | 
|  | EXPECT_EQ(w, GetParameterBlock(1)->user_state()); | 
|  |  | 
|  | // z | 
|  | problem->RemoveParameterBlock(z); | 
|  | ASSERT_EQ(1, problem->NumParameterBlocks()); | 
|  | ASSERT_EQ(0, problem->NumResidualBlocks()); | 
|  | EXPECT_EQ(w, GetParameterBlock(0)->user_state()); | 
|  |  | 
|  | // w | 
|  | problem->RemoveParameterBlock(w); | 
|  | EXPECT_EQ(0, problem->NumParameterBlocks()); | 
|  | EXPECT_EQ(0, problem->NumResidualBlocks()); | 
|  | } | 
|  |  | 
|  | TEST_P(DynamicProblem, RemoveParameterBlockWithResiduals) { | 
|  | problem->AddParameterBlock(y, 4); | 
|  | problem->AddParameterBlock(z, 5); | 
|  | problem->AddParameterBlock(w, 3); | 
|  | ASSERT_EQ(3, problem->NumParameterBlocks()); | 
|  | ASSERT_EQ(0, problem->NumResidualBlocks()); | 
|  | EXPECT_EQ(y, GetParameterBlock(0)->user_state()); | 
|  | EXPECT_EQ(z, GetParameterBlock(1)->user_state()); | 
|  | EXPECT_EQ(w, GetParameterBlock(2)->user_state()); | 
|  |  | 
|  | // Add all combinations of cost functions. | 
|  | CostFunction* cost_yzw = new TernaryCostFunction(1, 4, 5, 3); | 
|  | CostFunction* cost_yz  = new BinaryCostFunction (1, 4, 5); | 
|  | CostFunction* cost_yw  = new BinaryCostFunction (1, 4, 3); | 
|  | CostFunction* cost_zw  = new BinaryCostFunction (1, 5, 3); | 
|  | CostFunction* cost_y   = new UnaryCostFunction  (1, 4); | 
|  | CostFunction* cost_z   = new UnaryCostFunction  (1, 5); | 
|  | CostFunction* cost_w   = new UnaryCostFunction  (1, 3); | 
|  |  | 
|  | ResidualBlock* r_yzw = problem->AddResidualBlock(cost_yzw, NULL, y, z, w); | 
|  | ResidualBlock* r_yz  = problem->AddResidualBlock(cost_yz,  NULL, y, z); | 
|  | ResidualBlock* r_yw  = problem->AddResidualBlock(cost_yw,  NULL, y, w); | 
|  | ResidualBlock* r_zw  = problem->AddResidualBlock(cost_zw,  NULL, z, w); | 
|  | ResidualBlock* r_y   = problem->AddResidualBlock(cost_y,   NULL, y); | 
|  | ResidualBlock* r_z   = problem->AddResidualBlock(cost_z,   NULL, z); | 
|  | ResidualBlock* r_w   = problem->AddResidualBlock(cost_w,   NULL, w); | 
|  |  | 
|  | EXPECT_EQ(3, problem->NumParameterBlocks()); | 
|  | EXPECT_EQ(7, problem->NumResidualBlocks()); | 
|  |  | 
|  | // Remove w, which should remove r_yzw, r_yw, r_zw, r_w. | 
|  | problem->RemoveParameterBlock(w); | 
|  | ASSERT_EQ(2, problem->NumParameterBlocks()); | 
|  | ASSERT_EQ(3, problem->NumResidualBlocks()); | 
|  |  | 
|  | ASSERT_FALSE(HasResidualBlock(r_yzw)); | 
|  | ASSERT_TRUE (HasResidualBlock(r_yz )); | 
|  | ASSERT_FALSE(HasResidualBlock(r_yw )); | 
|  | ASSERT_FALSE(HasResidualBlock(r_zw )); | 
|  | ASSERT_TRUE (HasResidualBlock(r_y  )); | 
|  | ASSERT_TRUE (HasResidualBlock(r_z  )); | 
|  | ASSERT_FALSE(HasResidualBlock(r_w  )); | 
|  |  | 
|  | // Remove z, which will remove almost everything else. | 
|  | problem->RemoveParameterBlock(z); | 
|  | ASSERT_EQ(1, problem->NumParameterBlocks()); | 
|  | ASSERT_EQ(1, problem->NumResidualBlocks()); | 
|  |  | 
|  | ASSERT_FALSE(HasResidualBlock(r_yzw)); | 
|  | ASSERT_FALSE(HasResidualBlock(r_yz )); | 
|  | ASSERT_FALSE(HasResidualBlock(r_yw )); | 
|  | ASSERT_FALSE(HasResidualBlock(r_zw )); | 
|  | ASSERT_TRUE (HasResidualBlock(r_y  )); | 
|  | ASSERT_FALSE(HasResidualBlock(r_z  )); | 
|  | ASSERT_FALSE(HasResidualBlock(r_w  )); | 
|  |  | 
|  | // Remove y; all gone. | 
|  | problem->RemoveParameterBlock(y); | 
|  | EXPECT_EQ(0, problem->NumParameterBlocks()); | 
|  | EXPECT_EQ(0, problem->NumResidualBlocks()); | 
|  | } | 
|  |  | 
|  | TEST_P(DynamicProblem, RemoveResidualBlock) { | 
|  | problem->AddParameterBlock(y, 4); | 
|  | problem->AddParameterBlock(z, 5); | 
|  | problem->AddParameterBlock(w, 3); | 
|  |  | 
|  | // Add all combinations of cost functions. | 
|  | CostFunction* cost_yzw = new TernaryCostFunction(1, 4, 5, 3); | 
|  | CostFunction* cost_yz  = new BinaryCostFunction (1, 4, 5); | 
|  | CostFunction* cost_yw  = new BinaryCostFunction (1, 4, 3); | 
|  | CostFunction* cost_zw  = new BinaryCostFunction (1, 5, 3); | 
|  | CostFunction* cost_y   = new UnaryCostFunction  (1, 4); | 
|  | CostFunction* cost_z   = new UnaryCostFunction  (1, 5); | 
|  | CostFunction* cost_w   = new UnaryCostFunction  (1, 3); | 
|  |  | 
|  | ResidualBlock* r_yzw = problem->AddResidualBlock(cost_yzw, NULL, y, z, w); | 
|  | ResidualBlock* r_yz  = problem->AddResidualBlock(cost_yz,  NULL, y, z); | 
|  | ResidualBlock* r_yw  = problem->AddResidualBlock(cost_yw,  NULL, y, w); | 
|  | ResidualBlock* r_zw  = problem->AddResidualBlock(cost_zw,  NULL, z, w); | 
|  | ResidualBlock* r_y   = problem->AddResidualBlock(cost_y,   NULL, y); | 
|  | ResidualBlock* r_z   = problem->AddResidualBlock(cost_z,   NULL, z); | 
|  | ResidualBlock* r_w   = problem->AddResidualBlock(cost_w,   NULL, w); | 
|  |  | 
|  | if (GetParam()) { | 
|  | // In this test parameterization, there should be back-pointers from the | 
|  | // parameter blocks to the residual blocks. | 
|  | ExpectParameterBlockContains(y, r_yzw, r_yz, r_yw, r_y); | 
|  | ExpectParameterBlockContains(z, r_yzw, r_yz, r_zw, r_z); | 
|  | ExpectParameterBlockContains(w, r_yzw, r_yw, r_zw, r_w); | 
|  | } else { | 
|  | // Otherwise, nothing. | 
|  | EXPECT_TRUE(GetParameterBlock(0)->mutable_residual_blocks() == NULL); | 
|  | EXPECT_TRUE(GetParameterBlock(1)->mutable_residual_blocks() == NULL); | 
|  | EXPECT_TRUE(GetParameterBlock(2)->mutable_residual_blocks() == NULL); | 
|  | } | 
|  | EXPECT_EQ(3, problem->NumParameterBlocks()); | 
|  | EXPECT_EQ(7, problem->NumResidualBlocks()); | 
|  |  | 
|  | // Remove each residual and check the state after each removal. | 
|  |  | 
|  | // Remove r_yzw. | 
|  | problem->RemoveResidualBlock(r_yzw); | 
|  | ASSERT_EQ(3, problem->NumParameterBlocks()); | 
|  | ASSERT_EQ(6, problem->NumResidualBlocks()); | 
|  | if (GetParam()) { | 
|  | ExpectParameterBlockContains(y, r_yz, r_yw, r_y); | 
|  | ExpectParameterBlockContains(z, r_yz, r_zw, r_z); | 
|  | ExpectParameterBlockContains(w, r_yw, r_zw, r_w); | 
|  | } | 
|  | ASSERT_TRUE (HasResidualBlock(r_yz )); | 
|  | ASSERT_TRUE (HasResidualBlock(r_yw )); | 
|  | ASSERT_TRUE (HasResidualBlock(r_zw )); | 
|  | ASSERT_TRUE (HasResidualBlock(r_y  )); | 
|  | ASSERT_TRUE (HasResidualBlock(r_z  )); | 
|  | ASSERT_TRUE (HasResidualBlock(r_w  )); | 
|  |  | 
|  | // Remove r_yw. | 
|  | problem->RemoveResidualBlock(r_yw); | 
|  | ASSERT_EQ(3, problem->NumParameterBlocks()); | 
|  | ASSERT_EQ(5, problem->NumResidualBlocks()); | 
|  | if (GetParam()) { | 
|  | ExpectParameterBlockContains(y, r_yz, r_y); | 
|  | ExpectParameterBlockContains(z, r_yz, r_zw, r_z); | 
|  | ExpectParameterBlockContains(w, r_zw, r_w); | 
|  | } | 
|  | ASSERT_TRUE (HasResidualBlock(r_yz )); | 
|  | ASSERT_TRUE (HasResidualBlock(r_zw )); | 
|  | ASSERT_TRUE (HasResidualBlock(r_y  )); | 
|  | ASSERT_TRUE (HasResidualBlock(r_z  )); | 
|  | ASSERT_TRUE (HasResidualBlock(r_w  )); | 
|  |  | 
|  | // Remove r_zw. | 
|  | problem->RemoveResidualBlock(r_zw); | 
|  | ASSERT_EQ(3, problem->NumParameterBlocks()); | 
|  | ASSERT_EQ(4, problem->NumResidualBlocks()); | 
|  | if (GetParam()) { | 
|  | ExpectParameterBlockContains(y, r_yz, r_y); | 
|  | ExpectParameterBlockContains(z, r_yz, r_z); | 
|  | ExpectParameterBlockContains(w, r_w); | 
|  | } | 
|  | ASSERT_TRUE (HasResidualBlock(r_yz )); | 
|  | ASSERT_TRUE (HasResidualBlock(r_y  )); | 
|  | ASSERT_TRUE (HasResidualBlock(r_z  )); | 
|  | ASSERT_TRUE (HasResidualBlock(r_w  )); | 
|  |  | 
|  | // Remove r_w. | 
|  | problem->RemoveResidualBlock(r_w); | 
|  | ASSERT_EQ(3, problem->NumParameterBlocks()); | 
|  | ASSERT_EQ(3, problem->NumResidualBlocks()); | 
|  | if (GetParam()) { | 
|  | ExpectParameterBlockContains(y, r_yz, r_y); | 
|  | ExpectParameterBlockContains(z, r_yz, r_z); | 
|  | ExpectParameterBlockContains(w); | 
|  | } | 
|  | ASSERT_TRUE (HasResidualBlock(r_yz )); | 
|  | ASSERT_TRUE (HasResidualBlock(r_y  )); | 
|  | ASSERT_TRUE (HasResidualBlock(r_z  )); | 
|  |  | 
|  | // Remove r_yz. | 
|  | problem->RemoveResidualBlock(r_yz); | 
|  | ASSERT_EQ(3, problem->NumParameterBlocks()); | 
|  | ASSERT_EQ(2, problem->NumResidualBlocks()); | 
|  | if (GetParam()) { | 
|  | ExpectParameterBlockContains(y, r_y); | 
|  | ExpectParameterBlockContains(z, r_z); | 
|  | ExpectParameterBlockContains(w); | 
|  | } | 
|  | ASSERT_TRUE (HasResidualBlock(r_y  )); | 
|  | ASSERT_TRUE (HasResidualBlock(r_z  )); | 
|  |  | 
|  | // Remove the last two. | 
|  | problem->RemoveResidualBlock(r_z); | 
|  | problem->RemoveResidualBlock(r_y); | 
|  | ASSERT_EQ(3, problem->NumParameterBlocks()); | 
|  | ASSERT_EQ(0, problem->NumResidualBlocks()); | 
|  | if (GetParam()) { | 
|  | ExpectParameterBlockContains(y); | 
|  | ExpectParameterBlockContains(z); | 
|  | ExpectParameterBlockContains(w); | 
|  | } | 
|  | } | 
|  |  | 
|  | INSTANTIATE_TEST_CASE_P(OptionsInstantiation, | 
|  | DynamicProblem, | 
|  | ::testing::Values(true, false)); | 
|  |  | 
|  | // Test for Problem::Evaluate | 
|  |  | 
|  | // r_i = i - (j + 1) * x_ij^2 | 
|  | template <int kNumResiduals, int kNumParameterBlocks> | 
|  | class QuadraticCostFunction : public CostFunction { | 
|  | public: | 
|  | QuadraticCostFunction() { | 
|  | CHECK_GT(kNumResiduals, 0); | 
|  | CHECK_GT(kNumParameterBlocks, 0); | 
|  | set_num_residuals(kNumResiduals); | 
|  | for (int i = 0; i < kNumParameterBlocks; ++i) { | 
|  | mutable_parameter_block_sizes()->push_back(kNumResiduals); | 
|  | } | 
|  | } | 
|  |  | 
|  | virtual bool Evaluate(double const* const* parameters, | 
|  | double* residuals, | 
|  | double** jacobians) const { | 
|  | for (int i = 0; i < kNumResiduals; ++i) { | 
|  | residuals[i] = i; | 
|  | for (int j = 0; j < kNumParameterBlocks; ++j) { | 
|  | residuals[i] -= (j + 1.0) * parameters[j][i] * parameters[j][i]; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (jacobians == NULL) { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | for (int j = 0; j < kNumParameterBlocks; ++j) { | 
|  | if (jacobians[j] != NULL) { | 
|  | MatrixRef(jacobians[j], kNumResiduals, kNumResiduals) = | 
|  | (-2.0 * (j + 1.0) * | 
|  | ConstVectorRef(parameters[j], kNumResiduals)).asDiagonal(); | 
|  | } | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  | }; | 
|  |  | 
|  | // Convert a CRSMatrix to a dense Eigen matrix. | 
|  | void CRSToDenseMatrix(const CRSMatrix& input, Matrix* output) { | 
|  | Matrix& m = *CHECK_NOTNULL(output); | 
|  | m.resize(input.num_rows, input.num_cols); | 
|  | m.setZero(); | 
|  | for (int row = 0; row < input.num_rows; ++row) { | 
|  | for (int j = input.rows[row]; j < input.rows[row + 1]; ++j) { | 
|  | const int col = input.cols[j]; | 
|  | m(row, col) = input.values[j]; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | class ProblemEvaluateTest : public ::testing::Test { | 
|  | protected: | 
|  | void SetUp() { | 
|  | for (int i = 0; i < 6; ++i) { | 
|  | parameters_[i] = static_cast<double>(i + 1); | 
|  | } | 
|  |  | 
|  | parameter_blocks_.push_back(parameters_); | 
|  | parameter_blocks_.push_back(parameters_ + 2); | 
|  | parameter_blocks_.push_back(parameters_ + 4); | 
|  |  | 
|  |  | 
|  | CostFunction* cost_function = new QuadraticCostFunction<2, 2>; | 
|  |  | 
|  | // f(x, y) | 
|  | residual_blocks_.push_back( | 
|  | problem_.AddResidualBlock(cost_function, | 
|  | NULL, | 
|  | parameters_, | 
|  | parameters_ + 2)); | 
|  | // g(y, z) | 
|  | residual_blocks_.push_back( | 
|  | problem_.AddResidualBlock(cost_function, | 
|  | NULL, parameters_ + 2, | 
|  | parameters_ + 4)); | 
|  | // h(z, x) | 
|  | residual_blocks_.push_back( | 
|  | problem_.AddResidualBlock(cost_function, | 
|  | NULL, | 
|  | parameters_ + 4, | 
|  | parameters_)); | 
|  | } | 
|  |  | 
|  |  | 
|  |  | 
|  | void EvaluateAndCompare(const Problem::EvaluateOptions& options, | 
|  | const int expected_num_rows, | 
|  | const int expected_num_cols, | 
|  | const double expected_cost, | 
|  | const double* expected_residuals, | 
|  | const double* expected_gradient, | 
|  | const double* expected_jacobian) { | 
|  | double cost; | 
|  | vector<double> residuals; | 
|  | vector<double> gradient; | 
|  | CRSMatrix jacobian; | 
|  |  | 
|  | EXPECT_TRUE( | 
|  | problem_.Evaluate(options, | 
|  | &cost, | 
|  | expected_residuals != NULL ? &residuals : NULL, | 
|  | expected_gradient != NULL ? &gradient : NULL, | 
|  | expected_jacobian != NULL ? &jacobian : NULL)); | 
|  |  | 
|  | if (expected_residuals != NULL) { | 
|  | EXPECT_EQ(residuals.size(), expected_num_rows); | 
|  | } | 
|  |  | 
|  | if (expected_gradient != NULL) { | 
|  | EXPECT_EQ(gradient.size(), expected_num_cols); | 
|  | } | 
|  |  | 
|  | if (expected_jacobian != NULL) { | 
|  | EXPECT_EQ(jacobian.num_rows, expected_num_rows); | 
|  | EXPECT_EQ(jacobian.num_cols, expected_num_cols); | 
|  | } | 
|  |  | 
|  | Matrix dense_jacobian; | 
|  | if (expected_jacobian != NULL) { | 
|  | CRSToDenseMatrix(jacobian, &dense_jacobian); | 
|  | } | 
|  |  | 
|  | CompareEvaluations(expected_num_rows, | 
|  | expected_num_cols, | 
|  | expected_cost, | 
|  | expected_residuals, | 
|  | expected_gradient, | 
|  | expected_jacobian, | 
|  | cost, | 
|  | residuals.size() > 0 ? &residuals[0] : NULL, | 
|  | gradient.size() > 0 ? &gradient[0] : NULL, | 
|  | dense_jacobian.data()); | 
|  | } | 
|  |  | 
|  | void CheckAllEvaluationCombinations(const Problem::EvaluateOptions& options, | 
|  | const ExpectedEvaluation& expected) { | 
|  | for (int i = 0; i < 8; ++i) { | 
|  | EvaluateAndCompare(options, | 
|  | expected.num_rows, | 
|  | expected.num_cols, | 
|  | expected.cost, | 
|  | (i & 1) ? expected.residuals : NULL, | 
|  | (i & 2) ? expected.gradient  : NULL, | 
|  | (i & 4) ? expected.jacobian  : NULL); | 
|  | } | 
|  | } | 
|  |  | 
|  | ProblemImpl problem_; | 
|  | double parameters_[6]; | 
|  | vector<double*> parameter_blocks_; | 
|  | vector<ResidualBlockId> residual_blocks_; | 
|  | }; | 
|  |  | 
|  |  | 
|  | TEST_F(ProblemEvaluateTest, MultipleParameterAndResidualBlocks) { | 
|  | ExpectedEvaluation expected = { | 
|  | // Rows/columns | 
|  | 6, 6, | 
|  | // Cost | 
|  | 7607.0, | 
|  | // Residuals | 
|  | { -19.0, -35.0,  // f | 
|  | -59.0, -87.0,  // g | 
|  | -27.0, -43.0   // h | 
|  | }, | 
|  | // Gradient | 
|  | {  146.0,  484.0,   // x | 
|  | 582.0, 1256.0,   // y | 
|  | 1450.0, 2604.0,   // z | 
|  | }, | 
|  | // Jacobian | 
|  | //                       x             y             z | 
|  | { /* f(x, y) */ -2.0,  0.0, -12.0,   0.0,   0.0,   0.0, | 
|  | 0.0, -4.0,   0.0, -16.0,   0.0,   0.0, | 
|  | /* g(y, z) */  0.0,  0.0,  -6.0,   0.0, -20.0,   0.0, | 
|  | 0.0,  0.0,   0.0,  -8.0,   0.0, -24.0, | 
|  | /* h(z, x) */ -4.0,  0.0,   0.0,   0.0, -10.0,   0.0, | 
|  | 0.0, -8.0,   0.0,   0.0,   0.0, -12.0 | 
|  | } | 
|  | }; | 
|  |  | 
|  | CheckAllEvaluationCombinations(Problem::EvaluateOptions(), expected); | 
|  | } | 
|  |  | 
|  | TEST_F(ProblemEvaluateTest, ParameterAndResidualBlocksPassedInOptions) { | 
|  | ExpectedEvaluation expected = { | 
|  | // Rows/columns | 
|  | 6, 6, | 
|  | // Cost | 
|  | 7607.0, | 
|  | // Residuals | 
|  | { -19.0, -35.0,  // f | 
|  | -59.0, -87.0,  // g | 
|  | -27.0, -43.0   // h | 
|  | }, | 
|  | // Gradient | 
|  | {  146.0,  484.0,   // x | 
|  | 582.0, 1256.0,   // y | 
|  | 1450.0, 2604.0,   // z | 
|  | }, | 
|  | // Jacobian | 
|  | //                       x             y             z | 
|  | { /* f(x, y) */ -2.0,  0.0, -12.0,   0.0,   0.0,   0.0, | 
|  | 0.0, -4.0,   0.0, -16.0,   0.0,   0.0, | 
|  | /* g(y, z) */  0.0,  0.0,  -6.0,   0.0, -20.0,   0.0, | 
|  | 0.0,  0.0,   0.0,  -8.0,   0.0, -24.0, | 
|  | /* h(z, x) */ -4.0,  0.0,   0.0,   0.0, -10.0,   0.0, | 
|  | 0.0, -8.0,   0.0,   0.0,   0.0, -12.0 | 
|  | } | 
|  | }; | 
|  |  | 
|  | Problem::EvaluateOptions evaluate_options; | 
|  | evaluate_options.parameter_blocks = parameter_blocks_; | 
|  | evaluate_options.residual_blocks = residual_blocks_; | 
|  | CheckAllEvaluationCombinations(evaluate_options, expected); | 
|  | } | 
|  |  | 
|  | TEST_F(ProblemEvaluateTest, ReorderedResidualBlocks) { | 
|  | ExpectedEvaluation expected = { | 
|  | // Rows/columns | 
|  | 6, 6, | 
|  | // Cost | 
|  | 7607.0, | 
|  | // Residuals | 
|  | { -19.0, -35.0,  // f | 
|  | -27.0, -43.0,  // h | 
|  | -59.0, -87.0   // g | 
|  | }, | 
|  | // Gradient | 
|  | {  146.0,  484.0,   // x | 
|  | 582.0, 1256.0,   // y | 
|  | 1450.0, 2604.0,   // z | 
|  | }, | 
|  | // Jacobian | 
|  | //                       x             y             z | 
|  | { /* f(x, y) */ -2.0,  0.0, -12.0,   0.0,   0.0,   0.0, | 
|  | 0.0, -4.0,   0.0, -16.0,   0.0,   0.0, | 
|  | /* h(z, x) */ -4.0,  0.0,   0.0,   0.0, -10.0,   0.0, | 
|  | 0.0, -8.0,   0.0,   0.0,   0.0, -12.0, | 
|  | /* g(y, z) */  0.0,  0.0,  -6.0,   0.0, -20.0,   0.0, | 
|  | 0.0,  0.0,   0.0,  -8.0,   0.0, -24.0 | 
|  | } | 
|  | }; | 
|  |  | 
|  | Problem::EvaluateOptions evaluate_options; | 
|  | evaluate_options.parameter_blocks = parameter_blocks_; | 
|  |  | 
|  | // f, h, g | 
|  | evaluate_options.residual_blocks.push_back(residual_blocks_[0]); | 
|  | evaluate_options.residual_blocks.push_back(residual_blocks_[2]); | 
|  | evaluate_options.residual_blocks.push_back(residual_blocks_[1]); | 
|  |  | 
|  | CheckAllEvaluationCombinations(evaluate_options, expected); | 
|  | } | 
|  |  | 
|  | TEST_F(ProblemEvaluateTest, ReorderedResidualBlocksAndReorderedParameterBlocks) { | 
|  | ExpectedEvaluation expected = { | 
|  | // Rows/columns | 
|  | 6, 6, | 
|  | // Cost | 
|  | 7607.0, | 
|  | // Residuals | 
|  | { -19.0, -35.0,  // f | 
|  | -27.0, -43.0,  // h | 
|  | -59.0, -87.0   // g | 
|  | }, | 
|  | // Gradient | 
|  | {  1450.0, 2604.0,   // z | 
|  | 582.0, 1256.0,   // y | 
|  | 146.0,  484.0,   // x | 
|  | }, | 
|  | // Jacobian | 
|  | //                       z             y             x | 
|  | { /* f(x, y) */   0.0,   0.0, -12.0,   0.0,  -2.0,   0.0, | 
|  | 0.0,   0.0,   0.0, -16.0,   0.0,  -4.0, | 
|  | /* h(z, x) */ -10.0,   0.0,   0.0,   0.0,  -4.0,   0.0, | 
|  | 0.0, -12.0,   0.0,   0.0,   0.0,  -8.0, | 
|  | /* g(y, z) */ -20.0,   0.0,  -6.0,   0.0,   0.0,   0.0, | 
|  | 0.0, -24.0,   0.0,  -8.0,   0.0,   0.0 | 
|  | } | 
|  | }; | 
|  |  | 
|  | Problem::EvaluateOptions evaluate_options; | 
|  | // z, y, x | 
|  | evaluate_options.parameter_blocks.push_back(parameter_blocks_[2]); | 
|  | evaluate_options.parameter_blocks.push_back(parameter_blocks_[1]); | 
|  | evaluate_options.parameter_blocks.push_back(parameter_blocks_[0]); | 
|  |  | 
|  | // f, h, g | 
|  | evaluate_options.residual_blocks.push_back(residual_blocks_[0]); | 
|  | evaluate_options.residual_blocks.push_back(residual_blocks_[2]); | 
|  | evaluate_options.residual_blocks.push_back(residual_blocks_[1]); | 
|  |  | 
|  | CheckAllEvaluationCombinations(evaluate_options, expected); | 
|  | } | 
|  |  | 
|  | TEST_F(ProblemEvaluateTest, ConstantParameterBlock) { | 
|  | ExpectedEvaluation expected = { | 
|  | // Rows/columns | 
|  | 6, 6, | 
|  | // Cost | 
|  | 7607.0, | 
|  | // Residuals | 
|  | { -19.0, -35.0,  // f | 
|  | -59.0, -87.0,  // g | 
|  | -27.0, -43.0   // h | 
|  | }, | 
|  |  | 
|  | // Gradient | 
|  | {  146.0,  484.0,  // x | 
|  | 0.0,    0.0,  // y | 
|  | 1450.0, 2604.0,  // z | 
|  | }, | 
|  |  | 
|  | // Jacobian | 
|  | //                       x             y             z | 
|  | { /* f(x, y) */ -2.0,  0.0,   0.0,   0.0,   0.0,   0.0, | 
|  | 0.0, -4.0,   0.0,   0.0,   0.0,   0.0, | 
|  | /* g(y, z) */  0.0,  0.0,   0.0,   0.0, -20.0,   0.0, | 
|  | 0.0,  0.0,   0.0,   0.0,   0.0, -24.0, | 
|  | /* h(z, x) */ -4.0,  0.0,   0.0,   0.0, -10.0,   0.0, | 
|  | 0.0, -8.0,   0.0,   0.0,   0.0, -12.0 | 
|  | } | 
|  | }; | 
|  |  | 
|  | problem_.SetParameterBlockConstant(parameters_ + 2); | 
|  | CheckAllEvaluationCombinations(Problem::EvaluateOptions(), expected); | 
|  | } | 
|  |  | 
|  | TEST_F(ProblemEvaluateTest, ExcludedAResidualBlock) { | 
|  | ExpectedEvaluation expected = { | 
|  | // Rows/columns | 
|  | 4, 6, | 
|  | // Cost | 
|  | 2082.0, | 
|  | // Residuals | 
|  | { -19.0, -35.0,  // f | 
|  | -27.0, -43.0   // h | 
|  | }, | 
|  | // Gradient | 
|  | {  146.0,  484.0,   // x | 
|  | 228.0,  560.0,   // y | 
|  | 270.0,  516.0,   // z | 
|  | }, | 
|  | // Jacobian | 
|  | //                       x             y             z | 
|  | { /* f(x, y) */ -2.0,  0.0, -12.0,   0.0,   0.0,   0.0, | 
|  | 0.0, -4.0,   0.0, -16.0,   0.0,   0.0, | 
|  | /* h(z, x) */ -4.0,  0.0,   0.0,   0.0, -10.0,   0.0, | 
|  | 0.0, -8.0,   0.0,   0.0,   0.0, -12.0 | 
|  | } | 
|  | }; | 
|  |  | 
|  | Problem::EvaluateOptions evaluate_options; | 
|  | evaluate_options.residual_blocks.push_back(residual_blocks_[0]); | 
|  | evaluate_options.residual_blocks.push_back(residual_blocks_[2]); | 
|  |  | 
|  | CheckAllEvaluationCombinations(evaluate_options, expected); | 
|  | } | 
|  |  | 
|  | TEST_F(ProblemEvaluateTest, ExcludedParameterBlock) { | 
|  | ExpectedEvaluation expected = { | 
|  | // Rows/columns | 
|  | 6, 4, | 
|  | // Cost | 
|  | 7607.0, | 
|  | // Residuals | 
|  | { -19.0, -35.0,  // f | 
|  | -59.0, -87.0,  // g | 
|  | -27.0, -43.0   // h | 
|  | }, | 
|  |  | 
|  | // Gradient | 
|  | {  146.0,  484.0,  // x | 
|  | 1450.0, 2604.0,  // z | 
|  | }, | 
|  |  | 
|  | // Jacobian | 
|  | //                       x             z | 
|  | { /* f(x, y) */ -2.0,  0.0,   0.0,   0.0, | 
|  | 0.0, -4.0,   0.0,   0.0, | 
|  | /* g(y, z) */  0.0,  0.0, -20.0,   0.0, | 
|  | 0.0,  0.0,   0.0, -24.0, | 
|  | /* h(z, x) */ -4.0,  0.0, -10.0,   0.0, | 
|  | 0.0, -8.0,   0.0, -12.0 | 
|  | } | 
|  | }; | 
|  |  | 
|  | Problem::EvaluateOptions evaluate_options; | 
|  | // x, z | 
|  | evaluate_options.parameter_blocks.push_back(parameter_blocks_[0]); | 
|  | evaluate_options.parameter_blocks.push_back(parameter_blocks_[2]); | 
|  | evaluate_options.residual_blocks = residual_blocks_; | 
|  | CheckAllEvaluationCombinations(evaluate_options, expected); | 
|  | } | 
|  |  | 
|  | TEST_F(ProblemEvaluateTest, ExcludedParameterBlockAndExcludedResidualBlock) { | 
|  | ExpectedEvaluation expected = { | 
|  | // Rows/columns | 
|  | 4, 4, | 
|  | // Cost | 
|  | 6318.0, | 
|  | // Residuals | 
|  | { -19.0, -35.0,  // f | 
|  | -59.0, -87.0,  // g | 
|  | }, | 
|  |  | 
|  | // Gradient | 
|  | {   38.0,  140.0,  // x | 
|  | 1180.0, 2088.0,  // z | 
|  | }, | 
|  |  | 
|  | // Jacobian | 
|  | //                       x             z | 
|  | { /* f(x, y) */ -2.0,  0.0,   0.0,   0.0, | 
|  | 0.0, -4.0,   0.0,   0.0, | 
|  | /* g(y, z) */  0.0,  0.0, -20.0,   0.0, | 
|  | 0.0,  0.0,   0.0, -24.0, | 
|  | } | 
|  | }; | 
|  |  | 
|  | Problem::EvaluateOptions evaluate_options; | 
|  | // x, z | 
|  | evaluate_options.parameter_blocks.push_back(parameter_blocks_[0]); | 
|  | evaluate_options.parameter_blocks.push_back(parameter_blocks_[2]); | 
|  | evaluate_options.residual_blocks.push_back(residual_blocks_[0]); | 
|  | evaluate_options.residual_blocks.push_back(residual_blocks_[1]); | 
|  |  | 
|  | CheckAllEvaluationCombinations(evaluate_options, expected); | 
|  | } | 
|  |  | 
|  | TEST_F(ProblemEvaluateTest, LocalParameterization) { | 
|  | ExpectedEvaluation expected = { | 
|  | // Rows/columns | 
|  | 6, 5, | 
|  | // Cost | 
|  | 7607.0, | 
|  | // Residuals | 
|  | { -19.0, -35.0,  // f | 
|  | -59.0, -87.0,  // g | 
|  | -27.0, -43.0   // h | 
|  | }, | 
|  | // Gradient | 
|  | {  146.0,  484.0,  // x | 
|  | 1256.0,          // y with SubsetParameterization | 
|  | 1450.0, 2604.0,  // z | 
|  | }, | 
|  | // Jacobian | 
|  | //                       x      y             z | 
|  | { /* f(x, y) */ -2.0,  0.0,   0.0,   0.0,   0.0, | 
|  | 0.0, -4.0, -16.0,   0.0,   0.0, | 
|  | /* g(y, z) */  0.0,  0.0,   0.0, -20.0,   0.0, | 
|  | 0.0,  0.0,  -8.0,   0.0, -24.0, | 
|  | /* h(z, x) */ -4.0,  0.0,   0.0, -10.0,   0.0, | 
|  | 0.0, -8.0,   0.0,   0.0, -12.0 | 
|  | } | 
|  | }; | 
|  |  | 
|  | vector<int> constant_parameters; | 
|  | constant_parameters.push_back(0); | 
|  | problem_.SetParameterization(parameters_ + 2, | 
|  | new SubsetParameterization(2, | 
|  | constant_parameters)); | 
|  |  | 
|  | CheckAllEvaluationCombinations(Problem::EvaluateOptions(), expected); | 
|  | } | 
|  |  | 
|  | }  // namespace internal | 
|  | }  // namespace ceres |