Blame internal/ceres/evaluator_test.cc

Packit ea1746
// Ceres Solver - A fast non-linear least squares minimizer
Packit ea1746
// Copyright 2015 Google Inc. All rights reserved.
Packit ea1746
// http://ceres-solver.org/
Packit ea1746
//
Packit ea1746
// Redistribution and use in source and binary forms, with or without
Packit ea1746
// modification, are permitted provided that the following conditions are met:
Packit ea1746
//
Packit ea1746
// * Redistributions of source code must retain the above copyright notice,
Packit ea1746
//   this list of conditions and the following disclaimer.
Packit ea1746
// * Redistributions in binary form must reproduce the above copyright notice,
Packit ea1746
//   this list of conditions and the following disclaimer in the documentation
Packit ea1746
//   and/or other materials provided with the distribution.
Packit ea1746
// * Neither the name of Google Inc. nor the names of its contributors may be
Packit ea1746
//   used to endorse or promote products derived from this software without
Packit ea1746
//   specific prior written permission.
Packit ea1746
//
Packit ea1746
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
Packit ea1746
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
Packit ea1746
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
Packit ea1746
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
Packit ea1746
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
Packit ea1746
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
Packit ea1746
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
Packit ea1746
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
Packit ea1746
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
Packit ea1746
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
Packit ea1746
// POSSIBILITY OF SUCH DAMAGE.
Packit ea1746
//
Packit ea1746
// Author: keir@google.com (Keir Mierle)
Packit ea1746
//
Packit ea1746
// Tests shared across evaluators. The tests try all combinations of linear
Packit ea1746
// solver and num_eliminate_blocks (for schur-based solvers).
Packit ea1746
Packit ea1746
#include "ceres/evaluator.h"
Packit ea1746
Packit ea1746
#include "ceres/casts.h"
Packit ea1746
#include "ceres/cost_function.h"
Packit ea1746
#include "ceres/crs_matrix.h"
Packit ea1746
#include "ceres/evaluator_test_utils.h"
Packit ea1746
#include "ceres/internal/eigen.h"
Packit ea1746
#include "ceres/internal/scoped_ptr.h"
Packit ea1746
#include "ceres/local_parameterization.h"
Packit ea1746
#include "ceres/problem_impl.h"
Packit ea1746
#include "ceres/program.h"
Packit ea1746
#include "ceres/sized_cost_function.h"
Packit ea1746
#include "ceres/sparse_matrix.h"
Packit ea1746
#include "ceres/stringprintf.h"
Packit ea1746
#include "ceres/types.h"
Packit ea1746
#include "gtest/gtest.h"
Packit ea1746
Packit ea1746
namespace ceres {
Packit ea1746
namespace internal {
Packit ea1746
Packit ea1746
using std::string;
Packit ea1746
using std::vector;
Packit ea1746
Packit ea1746
// TODO(keir): Consider pushing this into a common test utils file.
Packit ea1746
template
Packit ea1746
         int N0 = 0, int N1 = 0, int N2 = 0, bool kSucceeds = true>
Packit ea1746
class ParameterIgnoringCostFunction
Packit ea1746
    : public SizedCostFunction<kNumResiduals, N0, N1, N2> {
Packit ea1746
  typedef SizedCostFunction<kNumResiduals, N0, N1, N2> Base;
Packit ea1746
 public:
Packit ea1746
  virtual bool Evaluate(double const* const* parameters,
Packit ea1746
                        double* residuals,
Packit ea1746
                        double** jacobians) const {
Packit ea1746
    for (int i = 0; i < Base::num_residuals(); ++i) {
Packit ea1746
      residuals[i] = i + 1;
Packit ea1746
    }
Packit ea1746
    if (jacobians) {
Packit ea1746
      for (int k = 0; k < Base::parameter_block_sizes().size(); ++k) {
Packit ea1746
        // The jacobians here are full sized, but they are transformed in the
Packit ea1746
        // evaluator into the "local" jacobian. In the tests, the "subset
Packit ea1746
        // constant" parameterization is used, which should pick out columns
Packit ea1746
        // from these jacobians. Put values in the jacobian that make this
Packit ea1746
        // obvious; in particular, make the jacobians like this:
Packit ea1746
        //
Packit ea1746
        //   1 2 3 4 ...
Packit ea1746
        //   1 2 3 4 ...   .*  kFactor
Packit ea1746
        //   1 2 3 4 ...
Packit ea1746
        //
Packit ea1746
        // where the multiplication by kFactor makes it easier to distinguish
Packit ea1746
        // between Jacobians of different residuals for the same parameter.
Packit ea1746
        if (jacobians[k] != NULL) {
Packit ea1746
          MatrixRef jacobian(jacobians[k],
Packit ea1746
                             Base::num_residuals(),
Packit ea1746
                             Base::parameter_block_sizes()[k]);
Packit ea1746
          for (int j = 0; j < Base::parameter_block_sizes()[k]; ++j) {
Packit ea1746
            jacobian.col(j).setConstant(kFactor * (j + 1));
Packit ea1746
          }
Packit ea1746
        }
Packit ea1746
      }
Packit ea1746
    }
Packit ea1746
    return kSucceeds;
Packit ea1746
  }
Packit ea1746
};
Packit ea1746
Packit ea1746
struct EvaluatorTestOptions {
Packit ea1746
  EvaluatorTestOptions(LinearSolverType linear_solver_type,
Packit ea1746
                       int num_eliminate_blocks,
Packit ea1746
                       bool dynamic_sparsity = false)
Packit ea1746
    : linear_solver_type(linear_solver_type),
Packit ea1746
      num_eliminate_blocks(num_eliminate_blocks),
Packit ea1746
      dynamic_sparsity(dynamic_sparsity) {}
Packit ea1746
Packit ea1746
  LinearSolverType linear_solver_type;
Packit ea1746
  int num_eliminate_blocks;
Packit ea1746
  bool dynamic_sparsity;
Packit ea1746
};
Packit ea1746
Packit ea1746
struct EvaluatorTest
Packit ea1746
    : public ::testing::TestWithParam<EvaluatorTestOptions> {
Packit ea1746
  Evaluator* CreateEvaluator(Program* program) {
Packit ea1746
    // This program is straight from the ProblemImpl, and so has no index/offset
Packit ea1746
    // yet; compute it here as required by the evalutor implementations.
Packit ea1746
    program->SetParameterOffsetsAndIndex();
Packit ea1746
Packit ea1746
    if (VLOG_IS_ON(1)) {
Packit ea1746
      string report;
Packit ea1746
      StringAppendF(&report, "Creating evaluator with type: %d",
Packit ea1746
                    GetParam().linear_solver_type);
Packit ea1746
      if (GetParam().linear_solver_type == SPARSE_NORMAL_CHOLESKY) {
Packit ea1746
        StringAppendF(&report, ", dynamic_sparsity: %d",
Packit ea1746
                      GetParam().dynamic_sparsity);
Packit ea1746
      }
Packit ea1746
      StringAppendF(&report, " and num_eliminate_blocks: %d",
Packit ea1746
                    GetParam().num_eliminate_blocks);
Packit ea1746
      VLOG(1) << report;
Packit ea1746
    }
Packit ea1746
    Evaluator::Options options;
Packit ea1746
    options.linear_solver_type = GetParam().linear_solver_type;
Packit ea1746
    options.num_eliminate_blocks = GetParam().num_eliminate_blocks;
Packit ea1746
    options.dynamic_sparsity = GetParam().dynamic_sparsity;
Packit ea1746
    string error;
Packit ea1746
    return Evaluator::Create(options, program, &error);
Packit ea1746
  }
Packit ea1746
Packit ea1746
  void EvaluateAndCompare(ProblemImpl *problem,
Packit ea1746
                          int expected_num_rows,
Packit ea1746
                          int expected_num_cols,
Packit ea1746
                          double expected_cost,
Packit ea1746
                          const double* expected_residuals,
Packit ea1746
                          const double* expected_gradient,
Packit ea1746
                          const double* expected_jacobian) {
Packit ea1746
    scoped_ptr<Evaluator> evaluator(
Packit ea1746
        CreateEvaluator(problem->mutable_program()));
Packit ea1746
    int num_residuals = expected_num_rows;
Packit ea1746
    int num_parameters = expected_num_cols;
Packit ea1746
Packit ea1746
    double cost = -1;
Packit ea1746
Packit ea1746
    Vector residuals(num_residuals);
Packit ea1746
    residuals.setConstant(-2000);
Packit ea1746
Packit ea1746
    Vector gradient(num_parameters);
Packit ea1746
    gradient.setConstant(-3000);
Packit ea1746
Packit ea1746
    scoped_ptr<SparseMatrix> jacobian(evaluator->CreateJacobian());
Packit ea1746
Packit ea1746
    ASSERT_EQ(expected_num_rows, evaluator->NumResiduals());
Packit ea1746
    ASSERT_EQ(expected_num_cols, evaluator->NumEffectiveParameters());
Packit ea1746
    ASSERT_EQ(expected_num_rows, jacobian->num_rows());
Packit ea1746
    ASSERT_EQ(expected_num_cols, jacobian->num_cols());
Packit ea1746
Packit ea1746
    vector<double> state(evaluator->NumParameters());
Packit ea1746
Packit ea1746
    ASSERT_TRUE(evaluator->Evaluate(
Packit ea1746
          &state[0],
Packit ea1746
          &cost,
Packit ea1746
          expected_residuals != NULL ? &residuals[0]  : NULL,
Packit ea1746
          expected_gradient  != NULL ? &gradient[0]   : NULL,
Packit ea1746
          expected_jacobian  != NULL ? jacobian.get() : NULL));
Packit ea1746
Packit ea1746
    Matrix actual_jacobian;
Packit ea1746
    if (expected_jacobian != NULL) {
Packit ea1746
      jacobian->ToDenseMatrix(&actual_jacobian);
Packit ea1746
    }
Packit ea1746
Packit ea1746
    CompareEvaluations(expected_num_rows,
Packit ea1746
                       expected_num_cols,
Packit ea1746
                       expected_cost,
Packit ea1746
                       expected_residuals,
Packit ea1746
                       expected_gradient,
Packit ea1746
                       expected_jacobian,
Packit ea1746
                       cost,
Packit ea1746
                       &residuals[0],
Packit ea1746
                       &gradient[0],
Packit ea1746
                       actual_jacobian.data());
Packit ea1746
  }
Packit ea1746
Packit ea1746
  // Try all combinations of parameters for the evaluator.
Packit ea1746
  void CheckAllEvaluationCombinations(const ExpectedEvaluation &expected) {
Packit ea1746
    for (int i = 0; i < 8; ++i) {
Packit ea1746
      EvaluateAndCompare(&problem,
Packit ea1746
                         expected.num_rows,
Packit ea1746
                         expected.num_cols,
Packit ea1746
                         expected.cost,
Packit ea1746
                         (i & 1) ? expected.residuals : NULL,
Packit ea1746
                         (i & 2) ? expected.gradient  : NULL,
Packit ea1746
                         (i & 4) ? expected.jacobian  : NULL);
Packit ea1746
    }
Packit ea1746
  }
Packit ea1746
Packit ea1746
  // The values are ignored completely by the cost function.
Packit ea1746
  double x[2];
Packit ea1746
  double y[3];
Packit ea1746
  double z[4];
Packit ea1746
Packit ea1746
  ProblemImpl problem;
Packit ea1746
};
Packit ea1746
Packit ea1746
void SetSparseMatrixConstant(SparseMatrix* sparse_matrix, double value) {
Packit ea1746
  VectorRef(sparse_matrix->mutable_values(),
Packit ea1746
            sparse_matrix->num_nonzeros()).setConstant(value);
Packit ea1746
}
Packit ea1746
Packit ea1746
TEST_P(EvaluatorTest, SingleResidualProblem) {
Packit ea1746
  problem.AddResidualBlock(new ParameterIgnoringCostFunction<1, 3, 2, 3, 4>,
Packit ea1746
                           NULL,
Packit ea1746
                           x, y, z);
Packit ea1746
Packit ea1746
  ExpectedEvaluation expected = {
Packit ea1746
    // Rows/columns
Packit ea1746
    3, 9,
Packit ea1746
    // Cost
Packit ea1746
    7.0,
Packit ea1746
    // Residuals
Packit ea1746
    { 1.0, 2.0, 3.0 },
Packit ea1746
    // Gradient
Packit ea1746
    { 6.0, 12.0,              // x
Packit ea1746
      6.0, 12.0, 18.0,        // y
Packit ea1746
      6.0, 12.0, 18.0, 24.0,  // z
Packit ea1746
    },
Packit ea1746
    // Jacobian
Packit ea1746
    //   x          y             z
Packit ea1746
    { 1, 2,   1, 2, 3,   1, 2, 3, 4,
Packit ea1746
      1, 2,   1, 2, 3,   1, 2, 3, 4,
Packit ea1746
      1, 2,   1, 2, 3,   1, 2, 3, 4
Packit ea1746
    }
Packit ea1746
  };
Packit ea1746
  CheckAllEvaluationCombinations(expected);
Packit ea1746
}
Packit ea1746
Packit ea1746
TEST_P(EvaluatorTest, SingleResidualProblemWithPermutedParameters) {
Packit ea1746
  // Add the parameters in explicit order to force the ordering in the program.
Packit ea1746
  problem.AddParameterBlock(x,  2);
Packit ea1746
  problem.AddParameterBlock(y,  3);
Packit ea1746
  problem.AddParameterBlock(z,  4);
Packit ea1746
Packit ea1746
  // Then use a cost function which is similar to the others, but swap around
Packit ea1746
  // the ordering of the parameters to the cost function. This shouldn't affect
Packit ea1746
  // the jacobian evaluation, but requires explicit handling in the evaluators.
Packit ea1746
  // At one point the compressed row evaluator had a bug that went undetected
Packit ea1746
  // for a long time, since by chance most users added parameters to the problem
Packit ea1746
  // in the same order that they occurred as parameters to a cost function.
Packit ea1746
  problem.AddResidualBlock(new ParameterIgnoringCostFunction<1, 3, 4, 3, 2>,
Packit ea1746
                           NULL,
Packit ea1746
                           z, y, x);
Packit ea1746
Packit ea1746
  ExpectedEvaluation expected = {
Packit ea1746
    // Rows/columns
Packit ea1746
    3, 9,
Packit ea1746
    // Cost
Packit ea1746
    7.0,
Packit ea1746
    // Residuals
Packit ea1746
    { 1.0, 2.0, 3.0 },
Packit ea1746
    // Gradient
Packit ea1746
    { 6.0, 12.0,              // x
Packit ea1746
      6.0, 12.0, 18.0,        // y
Packit ea1746
      6.0, 12.0, 18.0, 24.0,  // z
Packit ea1746
    },
Packit ea1746
    // Jacobian
Packit ea1746
    //   x          y             z
Packit ea1746
    { 1, 2,   1, 2, 3,   1, 2, 3, 4,
Packit ea1746
      1, 2,   1, 2, 3,   1, 2, 3, 4,
Packit ea1746
      1, 2,   1, 2, 3,   1, 2, 3, 4
Packit ea1746
    }
Packit ea1746
  };
Packit ea1746
  CheckAllEvaluationCombinations(expected);
Packit ea1746
}
Packit ea1746
Packit ea1746
TEST_P(EvaluatorTest, SingleResidualProblemWithNuisanceParameters) {
Packit ea1746
  // These parameters are not used.
Packit ea1746
  double a[2];
Packit ea1746
  double b[1];
Packit ea1746
  double c[1];
Packit ea1746
  double d[3];
Packit ea1746
Packit ea1746
  // Add the parameters in a mixed order so the Jacobian is "checkered" with the
Packit ea1746
  // values from the other parameters.
Packit ea1746
  problem.AddParameterBlock(a, 2);
Packit ea1746
  problem.AddParameterBlock(x, 2);
Packit ea1746
  problem.AddParameterBlock(b, 1);
Packit ea1746
  problem.AddParameterBlock(y, 3);
Packit ea1746
  problem.AddParameterBlock(c, 1);
Packit ea1746
  problem.AddParameterBlock(z, 4);
Packit ea1746
  problem.AddParameterBlock(d, 3);
Packit ea1746
Packit ea1746
  problem.AddResidualBlock(new ParameterIgnoringCostFunction<1, 3, 2, 3, 4>,
Packit ea1746
                           NULL,
Packit ea1746
                           x, y, z);
Packit ea1746
Packit ea1746
  ExpectedEvaluation expected = {
Packit ea1746
    // Rows/columns
Packit ea1746
    3, 16,
Packit ea1746
    // Cost
Packit ea1746
    7.0,
Packit ea1746
    // Residuals
Packit ea1746
    { 1.0, 2.0, 3.0 },
Packit ea1746
    // Gradient
Packit ea1746
    { 0.0, 0.0,               // a
Packit ea1746
      6.0, 12.0,              // x
Packit ea1746
      0.0,                    // b
Packit ea1746
      6.0, 12.0, 18.0,        // y
Packit ea1746
      0.0,                    // c
Packit ea1746
      6.0, 12.0, 18.0, 24.0,  // z
Packit ea1746
      0.0, 0.0, 0.0,          // d
Packit ea1746
    },
Packit ea1746
    // Jacobian
Packit ea1746
    //   a        x     b           y     c              z           d
Packit ea1746
    { 0, 0,    1, 2,    0,    1, 2, 3,    0,    1, 2, 3, 4,    0, 0, 0,
Packit ea1746
      0, 0,    1, 2,    0,    1, 2, 3,    0,    1, 2, 3, 4,    0, 0, 0,
Packit ea1746
      0, 0,    1, 2,    0,    1, 2, 3,    0,    1, 2, 3, 4,    0, 0, 0
Packit ea1746
    }
Packit ea1746
  };
Packit ea1746
  CheckAllEvaluationCombinations(expected);
Packit ea1746
}
Packit ea1746
Packit ea1746
TEST_P(EvaluatorTest, MultipleResidualProblem) {
Packit ea1746
  // Add the parameters in explicit order to force the ordering in the program.
Packit ea1746
  problem.AddParameterBlock(x,  2);
Packit ea1746
  problem.AddParameterBlock(y,  3);
Packit ea1746
  problem.AddParameterBlock(z,  4);
Packit ea1746
Packit ea1746
  // f(x, y) in R^2
Packit ea1746
  problem.AddResidualBlock(new ParameterIgnoringCostFunction<1, 2, 2, 3>,
Packit ea1746
                           NULL,
Packit ea1746
                           x, y);
Packit ea1746
Packit ea1746
  // g(x, z) in R^3
Packit ea1746
  problem.AddResidualBlock(new ParameterIgnoringCostFunction<2, 3, 2, 4>,
Packit ea1746
                           NULL,
Packit ea1746
                           x, z);
Packit ea1746
Packit ea1746
  // h(y, z) in R^4
Packit ea1746
  problem.AddResidualBlock(new ParameterIgnoringCostFunction<3, 4, 3, 4>,
Packit ea1746
                           NULL,
Packit ea1746
                           y, z);
Packit ea1746
Packit ea1746
  ExpectedEvaluation expected = {
Packit ea1746
    // Rows/columns
Packit ea1746
    9, 9,
Packit ea1746
    // Cost
Packit ea1746
    // f       g           h
Packit ea1746
    (  1 + 4 + 1 + 4 + 9 + 1 + 4 + 9 + 16) / 2.0,
Packit ea1746
    // Residuals
Packit ea1746
    { 1.0, 2.0,           // f
Packit ea1746
      1.0, 2.0, 3.0,      // g
Packit ea1746
      1.0, 2.0, 3.0, 4.0  // h
Packit ea1746
    },
Packit ea1746
    // Gradient
Packit ea1746
    { 15.0, 30.0,               // x
Packit ea1746
      33.0, 66.0, 99.0,         // y
Packit ea1746
      42.0, 84.0, 126.0, 168.0  // z
Packit ea1746
    },
Packit ea1746
    // Jacobian
Packit ea1746
    //                x        y           z
Packit ea1746
    {   /* f(x, y) */ 1, 2,    1, 2, 3,    0, 0, 0, 0,
Packit ea1746
                      1, 2,    1, 2, 3,    0, 0, 0, 0,
Packit ea1746
Packit ea1746
        /* g(x, z) */ 2, 4,    0, 0, 0,    2, 4, 6, 8,
Packit ea1746
                      2, 4,    0, 0, 0,    2, 4, 6, 8,
Packit ea1746
                      2, 4,    0, 0, 0,    2, 4, 6, 8,
Packit ea1746
Packit ea1746
        /* h(y, z) */ 0, 0,    3, 6, 9,    3, 6, 9, 12,
Packit ea1746
                      0, 0,    3, 6, 9,    3, 6, 9, 12,
Packit ea1746
                      0, 0,    3, 6, 9,    3, 6, 9, 12,
Packit ea1746
                      0, 0,    3, 6, 9,    3, 6, 9, 12
Packit ea1746
    }
Packit ea1746
  };
Packit ea1746
  CheckAllEvaluationCombinations(expected);
Packit ea1746
}
Packit ea1746
Packit ea1746
TEST_P(EvaluatorTest, MultipleResidualsWithLocalParameterizations) {
Packit ea1746
  // Add the parameters in explicit order to force the ordering in the program.
Packit ea1746
  problem.AddParameterBlock(x,  2);
Packit ea1746
Packit ea1746
  // Fix y's first dimension.
Packit ea1746
  vector<int> y_fixed;
Packit ea1746
  y_fixed.push_back(0);
Packit ea1746
  problem.AddParameterBlock(y, 3, new SubsetParameterization(3, y_fixed));
Packit ea1746
Packit ea1746
  // Fix z's second dimension.
Packit ea1746
  vector<int> z_fixed;
Packit ea1746
  z_fixed.push_back(1);
Packit ea1746
  problem.AddParameterBlock(z, 4, new SubsetParameterization(4, z_fixed));
Packit ea1746
Packit ea1746
  // f(x, y) in R^2
Packit ea1746
  problem.AddResidualBlock(new ParameterIgnoringCostFunction<1, 2, 2, 3>,
Packit ea1746
                           NULL,
Packit ea1746
                           x, y);
Packit ea1746
Packit ea1746
  // g(x, z) in R^3
Packit ea1746
  problem.AddResidualBlock(new ParameterIgnoringCostFunction<2, 3, 2, 4>,
Packit ea1746
                           NULL,
Packit ea1746
                           x, z);
Packit ea1746
Packit ea1746
  // h(y, z) in R^4
Packit ea1746
  problem.AddResidualBlock(new ParameterIgnoringCostFunction<3, 4, 3, 4>,
Packit ea1746
                           NULL,
Packit ea1746
                           y, z);
Packit ea1746
Packit ea1746
  ExpectedEvaluation expected = {
Packit ea1746
    // Rows/columns
Packit ea1746
    9, 7,
Packit ea1746
    // Cost
Packit ea1746
    // f       g           h
Packit ea1746
    (  1 + 4 + 1 + 4 + 9 + 1 + 4 + 9 + 16) / 2.0,
Packit ea1746
    // Residuals
Packit ea1746
    { 1.0, 2.0,           // f
Packit ea1746
      1.0, 2.0, 3.0,      // g
Packit ea1746
      1.0, 2.0, 3.0, 4.0  // h
Packit ea1746
    },
Packit ea1746
    // Gradient
Packit ea1746
    { 15.0, 30.0,         // x
Packit ea1746
      66.0, 99.0,         // y
Packit ea1746
      42.0, 126.0, 168.0  // z
Packit ea1746
    },
Packit ea1746
    // Jacobian
Packit ea1746
    //                x        y           z
Packit ea1746
    {   /* f(x, y) */ 1, 2,    2, 3,    0, 0, 0,
Packit ea1746
                      1, 2,    2, 3,    0, 0, 0,
Packit ea1746
Packit ea1746
        /* g(x, z) */ 2, 4,    0, 0,    2, 6, 8,
Packit ea1746
                      2, 4,    0, 0,    2, 6, 8,
Packit ea1746
                      2, 4,    0, 0,    2, 6, 8,
Packit ea1746
Packit ea1746
        /* h(y, z) */ 0, 0,    6, 9,    3, 9, 12,
Packit ea1746
                      0, 0,    6, 9,    3, 9, 12,
Packit ea1746
                      0, 0,    6, 9,    3, 9, 12,
Packit ea1746
                      0, 0,    6, 9,    3, 9, 12
Packit ea1746
    }
Packit ea1746
  };
Packit ea1746
  CheckAllEvaluationCombinations(expected);
Packit ea1746
}
Packit ea1746
Packit ea1746
TEST_P(EvaluatorTest, MultipleResidualProblemWithSomeConstantParameters) {
Packit ea1746
  // The values are ignored completely by the cost function.
Packit ea1746
  double x[2];
Packit ea1746
  double y[3];
Packit ea1746
  double z[4];
Packit ea1746
Packit ea1746
  // Add the parameters in explicit order to force the ordering in the program.
Packit ea1746
  problem.AddParameterBlock(x,  2);
Packit ea1746
  problem.AddParameterBlock(y,  3);
Packit ea1746
  problem.AddParameterBlock(z,  4);
Packit ea1746
Packit ea1746
  // f(x, y) in R^2
Packit ea1746
  problem.AddResidualBlock(new ParameterIgnoringCostFunction<1, 2, 2, 3>,
Packit ea1746
                           NULL,
Packit ea1746
                           x, y);
Packit ea1746
Packit ea1746
  // g(x, z) in R^3
Packit ea1746
  problem.AddResidualBlock(new ParameterIgnoringCostFunction<2, 3, 2, 4>,
Packit ea1746
                           NULL,
Packit ea1746
                           x, z);
Packit ea1746
Packit ea1746
  // h(y, z) in R^4
Packit ea1746
  problem.AddResidualBlock(new ParameterIgnoringCostFunction<3, 4, 3, 4>,
Packit ea1746
                           NULL,
Packit ea1746
                           y, z);
Packit ea1746
Packit ea1746
  // For this test, "z" is constant.
Packit ea1746
  problem.SetParameterBlockConstant(z);
Packit ea1746
Packit ea1746
  // Create the reduced program which is missing the fixed "z" variable.
Packit ea1746
  // Normally, the preprocessing of the program that happens in solver_impl
Packit ea1746
  // takes care of this, but we don't want to invoke the solver here.
Packit ea1746
  Program reduced_program;
Packit ea1746
  vector<ParameterBlock*>* parameter_blocks =
Packit ea1746
      problem.mutable_program()->mutable_parameter_blocks();
Packit ea1746
Packit ea1746
  // "z" is the last parameter; save it for later and pop it off temporarily.
Packit ea1746
  // Note that "z" will still get read during evaluation, so it cannot be
Packit ea1746
  // deleted at this point.
Packit ea1746
  ParameterBlock* parameter_block_z = parameter_blocks->back();
Packit ea1746
  parameter_blocks->pop_back();
Packit ea1746
Packit ea1746
  ExpectedEvaluation expected = {
Packit ea1746
    // Rows/columns
Packit ea1746
    9, 5,
Packit ea1746
    // Cost
Packit ea1746
    // f       g           h
Packit ea1746
    (  1 + 4 + 1 + 4 + 9 + 1 + 4 + 9 + 16) / 2.0,
Packit ea1746
    // Residuals
Packit ea1746
    { 1.0, 2.0,           // f
Packit ea1746
      1.0, 2.0, 3.0,      // g
Packit ea1746
      1.0, 2.0, 3.0, 4.0  // h
Packit ea1746
    },
Packit ea1746
    // Gradient
Packit ea1746
    { 15.0, 30.0,        // x
Packit ea1746
      33.0, 66.0, 99.0,  // y
Packit ea1746
    },
Packit ea1746
    // Jacobian
Packit ea1746
    //                x        y
Packit ea1746
    {   /* f(x, y) */ 1, 2,    1, 2, 3,
Packit ea1746
                      1, 2,    1, 2, 3,
Packit ea1746
Packit ea1746
        /* g(x, z) */ 2, 4,    0, 0, 0,
Packit ea1746
                      2, 4,    0, 0, 0,
Packit ea1746
                      2, 4,    0, 0, 0,
Packit ea1746
Packit ea1746
        /* h(y, z) */ 0, 0,    3, 6, 9,
Packit ea1746
                      0, 0,    3, 6, 9,
Packit ea1746
                      0, 0,    3, 6, 9,
Packit ea1746
                      0, 0,    3, 6, 9
Packit ea1746
    }
Packit ea1746
  };
Packit ea1746
  CheckAllEvaluationCombinations(expected);
Packit ea1746
Packit ea1746
  // Restore parameter block z, so it will get freed in a consistent way.
Packit ea1746
  parameter_blocks->push_back(parameter_block_z);
Packit ea1746
}
Packit ea1746
Packit ea1746
TEST_P(EvaluatorTest, EvaluatorAbortsForResidualsThatFailToEvaluate) {
Packit ea1746
  // Switch the return value to failure.
Packit ea1746
  problem.AddResidualBlock(
Packit ea1746
      new ParameterIgnoringCostFunction<20, 3, 2, 3, 4, false>, NULL, x, y, z);
Packit ea1746
Packit ea1746
  // The values are ignored.
Packit ea1746
  double state[9];
Packit ea1746
Packit ea1746
  scoped_ptr<Evaluator> evaluator(CreateEvaluator(problem.mutable_program()));
Packit ea1746
  scoped_ptr<SparseMatrix> jacobian(evaluator->CreateJacobian());
Packit ea1746
  double cost;
Packit ea1746
  EXPECT_FALSE(evaluator->Evaluate(state, &cost, NULL, NULL, NULL));
Packit ea1746
}
Packit ea1746
Packit ea1746
// In the pairs, the first argument is the linear solver type, and the second
Packit ea1746
// argument is num_eliminate_blocks. Changing the num_eliminate_blocks only
Packit ea1746
// makes sense for the schur-based solvers.
Packit ea1746
//
Packit ea1746
// Try all values of num_eliminate_blocks that make sense given that in the
Packit ea1746
// tests a maximum of 4 parameter blocks are present.
Packit ea1746
INSTANTIATE_TEST_CASE_P(
Packit ea1746
    LinearSolvers,
Packit ea1746
    EvaluatorTest,
Packit ea1746
    ::testing::Values(
Packit ea1746
      EvaluatorTestOptions(DENSE_QR, 0),
Packit ea1746
      EvaluatorTestOptions(DENSE_SCHUR, 0),
Packit ea1746
      EvaluatorTestOptions(DENSE_SCHUR, 1),
Packit ea1746
      EvaluatorTestOptions(DENSE_SCHUR, 2),
Packit ea1746
      EvaluatorTestOptions(DENSE_SCHUR, 3),
Packit ea1746
      EvaluatorTestOptions(DENSE_SCHUR, 4),
Packit ea1746
      EvaluatorTestOptions(SPARSE_SCHUR, 0),
Packit ea1746
      EvaluatorTestOptions(SPARSE_SCHUR, 1),
Packit ea1746
      EvaluatorTestOptions(SPARSE_SCHUR, 2),
Packit ea1746
      EvaluatorTestOptions(SPARSE_SCHUR, 3),
Packit ea1746
      EvaluatorTestOptions(SPARSE_SCHUR, 4),
Packit ea1746
      EvaluatorTestOptions(ITERATIVE_SCHUR, 0),
Packit ea1746
      EvaluatorTestOptions(ITERATIVE_SCHUR, 1),
Packit ea1746
      EvaluatorTestOptions(ITERATIVE_SCHUR, 2),
Packit ea1746
      EvaluatorTestOptions(ITERATIVE_SCHUR, 3),
Packit ea1746
      EvaluatorTestOptions(ITERATIVE_SCHUR, 4),
Packit ea1746
      EvaluatorTestOptions(SPARSE_NORMAL_CHOLESKY, 0, false),
Packit ea1746
      EvaluatorTestOptions(SPARSE_NORMAL_CHOLESKY, 0, true)));
Packit ea1746
Packit ea1746
// Simple cost function used to check if the evaluator is sensitive to
Packit ea1746
// state changes.
Packit ea1746
class ParameterSensitiveCostFunction : public SizedCostFunction<2, 2> {
Packit ea1746
 public:
Packit ea1746
  virtual bool Evaluate(double const* const* parameters,
Packit ea1746
                        double* residuals,
Packit ea1746
                        double** jacobians) const {
Packit ea1746
    double x1 = parameters[0][0];
Packit ea1746
    double x2 = parameters[0][1];
Packit ea1746
    residuals[0] = x1 * x1;
Packit ea1746
    residuals[1] = x2 * x2;
Packit ea1746
Packit ea1746
    if (jacobians != NULL) {
Packit ea1746
      double* jacobian = jacobians[0];
Packit ea1746
      if (jacobian != NULL) {
Packit ea1746
        jacobian[0] = 2.0 * x1;
Packit ea1746
        jacobian[1] = 0.0;
Packit ea1746
        jacobian[2] = 0.0;
Packit ea1746
        jacobian[3] = 2.0 * x2;
Packit ea1746
      }
Packit ea1746
    }
Packit ea1746
    return true;
Packit ea1746
  }
Packit ea1746
};
Packit ea1746
Packit ea1746
TEST(Evaluator, EvaluatorRespectsParameterChanges) {
Packit ea1746
  ProblemImpl problem;
Packit ea1746
Packit ea1746
  double x[2];
Packit ea1746
  x[0] = 1.0;
Packit ea1746
  x[1] = 1.0;
Packit ea1746
Packit ea1746
  problem.AddResidualBlock(new ParameterSensitiveCostFunction(), NULL, x);
Packit ea1746
  Program* program = problem.mutable_program();
Packit ea1746
  program->SetParameterOffsetsAndIndex();
Packit ea1746
Packit ea1746
  Evaluator::Options options;
Packit ea1746
  options.linear_solver_type = DENSE_QR;
Packit ea1746
  options.num_eliminate_blocks = 0;
Packit ea1746
  string error;
Packit ea1746
  scoped_ptr<Evaluator> evaluator(Evaluator::Create(options, program, &error));
Packit ea1746
  scoped_ptr<SparseMatrix> jacobian(evaluator->CreateJacobian());
Packit ea1746
Packit ea1746
  ASSERT_EQ(2, jacobian->num_rows());
Packit ea1746
  ASSERT_EQ(2, jacobian->num_cols());
Packit ea1746
Packit ea1746
  double state[2];
Packit ea1746
  state[0] = 2.0;
Packit ea1746
  state[1] = 3.0;
Packit ea1746
Packit ea1746
  // The original state of a residual block comes from the user's
Packit ea1746
  // state. So the original state is 1.0, 1.0, and the only way we get
Packit ea1746
  // the 2.0, 3.0 results in the following tests is if it respects the
Packit ea1746
  // values in the state vector.
Packit ea1746
Packit ea1746
  // Cost only; no residuals and no jacobian.
Packit ea1746
  {
Packit ea1746
    double cost = -1;
Packit ea1746
    ASSERT_TRUE(evaluator->Evaluate(state, &cost, NULL, NULL, NULL));
Packit ea1746
    EXPECT_EQ(48.5, cost);
Packit ea1746
  }
Packit ea1746
Packit ea1746
  // Cost and residuals, no jacobian.
Packit ea1746
  {
Packit ea1746
    double cost = -1;
Packit ea1746
    double residuals[2] = { -2, -2 };
Packit ea1746
    ASSERT_TRUE(evaluator->Evaluate(state, &cost, residuals, NULL, NULL));
Packit ea1746
    EXPECT_EQ(48.5, cost);
Packit ea1746
    EXPECT_EQ(4, residuals[0]);
Packit ea1746
    EXPECT_EQ(9, residuals[1]);
Packit ea1746
  }
Packit ea1746
Packit ea1746
  // Cost, residuals, and jacobian.
Packit ea1746
  {
Packit ea1746
    double cost = -1;
Packit ea1746
    double residuals[2] = { -2, -2};
Packit ea1746
    SetSparseMatrixConstant(jacobian.get(), -1);
Packit ea1746
    ASSERT_TRUE(evaluator->Evaluate(state,
Packit ea1746
                                    &cost,
Packit ea1746
                                    residuals,
Packit ea1746
                                    NULL,
Packit ea1746
                                    jacobian.get()));
Packit ea1746
    EXPECT_EQ(48.5, cost);
Packit ea1746
    EXPECT_EQ(4, residuals[0]);
Packit ea1746
    EXPECT_EQ(9, residuals[1]);
Packit ea1746
    Matrix actual_jacobian;
Packit ea1746
    jacobian->ToDenseMatrix(&actual_jacobian);
Packit ea1746
Packit ea1746
    Matrix expected_jacobian(2, 2);
Packit ea1746
    expected_jacobian
Packit ea1746
        << 2 * state[0], 0,
Packit ea1746
           0, 2 * state[1];
Packit ea1746
Packit ea1746
    EXPECT_TRUE((actual_jacobian.array() == expected_jacobian.array()).all())
Packit ea1746
        << "Actual:\n" << actual_jacobian
Packit ea1746
        << "\nExpected:\n" << expected_jacobian;
Packit ea1746
  }
Packit ea1746
}
Packit ea1746
Packit ea1746
}  // namespace internal
Packit ea1746
}  // namespace ceres