Blame internal/ceres/gradient_checker_test.cc

Packit ea1746
// Ceres Solver - A fast non-linear least squares minimizer
Packit ea1746
// Copyright 2016 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: wjr@google.com (William Rucklidge)
Packit ea1746
//
Packit ea1746
// This file contains tests for the GradientChecker class.
Packit ea1746
Packit ea1746
#include "ceres/gradient_checker.h"
Packit ea1746
Packit ea1746
#include <cmath>
Packit ea1746
#include <cstdlib>
Packit ea1746
#include <vector>
Packit ea1746
Packit ea1746
#include "ceres/cost_function.h"
Packit ea1746
#include "ceres/problem.h"
Packit ea1746
#include "ceres/random.h"
Packit ea1746
#include "ceres/solver.h"
Packit ea1746
#include "ceres/test_util.h"
Packit ea1746
#include "glog/logging.h"
Packit ea1746
#include "gtest/gtest.h"
Packit ea1746
Packit ea1746
namespace ceres {
Packit ea1746
namespace internal {
Packit ea1746
Packit ea1746
using std::vector;
Packit ea1746
Packit ea1746
// We pick a (non-quadratic) function whose derivative are easy:
Packit ea1746
//
Packit ea1746
//    f = exp(- a' x).
Packit ea1746
//   df = - f a.
Packit ea1746
//
Packit ea1746
// where 'a' is a vector of the same size as 'x'. In the block
Packit ea1746
// version, they are both block vectors, of course.
Packit ea1746
class GoodTestTerm : public CostFunction {
Packit ea1746
 public:
Packit ea1746
  GoodTestTerm(int arity, int const* dim) : arity_(arity), return_value_(true) {
Packit ea1746
    // Make 'arity' random vectors.
Packit ea1746
    a_.resize(arity_);
Packit ea1746
    for (int j = 0; j < arity_; ++j) {
Packit ea1746
      a_[j].resize(dim[j]);
Packit ea1746
      for (int u = 0; u < dim[j]; ++u) {
Packit ea1746
        a_[j][u] = 2.0 * RandDouble() - 1.0;
Packit ea1746
      }
Packit ea1746
    }
Packit ea1746
Packit ea1746
    for (int i = 0; i < arity_; i++) {
Packit ea1746
      mutable_parameter_block_sizes()->push_back(dim[i]);
Packit ea1746
    }
Packit ea1746
    set_num_residuals(1);
Packit ea1746
  }
Packit ea1746
Packit ea1746
  bool Evaluate(double const* const* parameters,
Packit ea1746
                double* residuals,
Packit ea1746
                double** jacobians) const {
Packit ea1746
    if (!return_value_) {
Packit ea1746
      return false;
Packit ea1746
    }
Packit ea1746
    // Compute a . x.
Packit ea1746
    double ax = 0;
Packit ea1746
    for (int j = 0; j < arity_; ++j) {
Packit ea1746
      for (int u = 0; u < parameter_block_sizes()[j]; ++u) {
Packit ea1746
        ax += a_[j][u] * parameters[j][u];
Packit ea1746
      }
Packit ea1746
    }
Packit ea1746
Packit ea1746
    // This is the cost, but also appears as a factor
Packit ea1746
    // in the derivatives.
Packit ea1746
    double f = *residuals = exp(-ax);
Packit ea1746
Packit ea1746
    // Accumulate 1st order derivatives.
Packit ea1746
    if (jacobians) {
Packit ea1746
      for (int j = 0; j < arity_; ++j) {
Packit ea1746
        if (jacobians[j]) {
Packit ea1746
          for (int u = 0; u < parameter_block_sizes()[j]; ++u) {
Packit ea1746
            // See comments before class.
Packit ea1746
            jacobians[j][u] = -f * a_[j][u];
Packit ea1746
          }
Packit ea1746
        }
Packit ea1746
      }
Packit ea1746
    }
Packit ea1746
Packit ea1746
    return true;
Packit ea1746
  }
Packit ea1746
Packit ea1746
  void SetReturnValue(bool return_value) { return_value_ = return_value; }
Packit ea1746
Packit ea1746
 private:
Packit ea1746
  int arity_;
Packit ea1746
  bool return_value_;
Packit ea1746
  vector<vector<double> > a_;  // our vectors.
Packit ea1746
};
Packit ea1746
Packit ea1746
class BadTestTerm : public CostFunction {
Packit ea1746
 public:
Packit ea1746
  BadTestTerm(int arity, int const* dim) : arity_(arity) {
Packit ea1746
    // Make 'arity' random vectors.
Packit ea1746
    a_.resize(arity_);
Packit ea1746
    for (int j = 0; j < arity_; ++j) {
Packit ea1746
      a_[j].resize(dim[j]);
Packit ea1746
      for (int u = 0; u < dim[j]; ++u) {
Packit ea1746
        a_[j][u] = 2.0 * RandDouble() - 1.0;
Packit ea1746
      }
Packit ea1746
    }
Packit ea1746
Packit ea1746
    for (int i = 0; i < arity_; i++) {
Packit ea1746
      mutable_parameter_block_sizes()->push_back(dim[i]);
Packit ea1746
    }
Packit ea1746
    set_num_residuals(1);
Packit ea1746
  }
Packit ea1746
Packit ea1746
  bool Evaluate(double const* const* parameters,
Packit ea1746
                double* residuals,
Packit ea1746
                double** jacobians) const {
Packit ea1746
    // Compute a . x.
Packit ea1746
    double ax = 0;
Packit ea1746
    for (int j = 0; j < arity_; ++j) {
Packit ea1746
      for (int u = 0; u < parameter_block_sizes()[j]; ++u) {
Packit ea1746
        ax += a_[j][u] * parameters[j][u];
Packit ea1746
      }
Packit ea1746
    }
Packit ea1746
Packit ea1746
    // This is the cost, but also appears as a factor
Packit ea1746
    // in the derivatives.
Packit ea1746
    double f = *residuals = exp(-ax);
Packit ea1746
Packit ea1746
    // Accumulate 1st order derivatives.
Packit ea1746
    if (jacobians) {
Packit ea1746
      for (int j = 0; j < arity_; ++j) {
Packit ea1746
        if (jacobians[j]) {
Packit ea1746
          for (int u = 0; u < parameter_block_sizes()[j]; ++u) {
Packit ea1746
            // See comments before class.
Packit ea1746
            jacobians[j][u] = -f * a_[j][u] + 0.001;
Packit ea1746
          }
Packit ea1746
        }
Packit ea1746
      }
Packit ea1746
    }
Packit ea1746
Packit ea1746
    return true;
Packit ea1746
  }
Packit ea1746
Packit ea1746
 private:
Packit ea1746
  int arity_;
Packit ea1746
  vector<vector<double> > a_;  // our vectors.
Packit ea1746
};
Packit ea1746
Packit ea1746
const double kTolerance = 1e-6;
Packit ea1746
Packit ea1746
void CheckDimensions(const GradientChecker::ProbeResults& results,
Packit ea1746
                     const std::vector<int>& parameter_sizes,
Packit ea1746
                     const std::vector<int>& local_parameter_sizes,
Packit ea1746
                     int residual_size) {
Packit ea1746
  CHECK_EQ(parameter_sizes.size(), local_parameter_sizes.size());
Packit ea1746
  int num_parameters = parameter_sizes.size();
Packit ea1746
  ASSERT_EQ(residual_size, results.residuals.size());
Packit ea1746
  ASSERT_EQ(num_parameters, results.local_jacobians.size());
Packit ea1746
  ASSERT_EQ(num_parameters, results.local_numeric_jacobians.size());
Packit ea1746
  ASSERT_EQ(num_parameters, results.jacobians.size());
Packit ea1746
  ASSERT_EQ(num_parameters, results.numeric_jacobians.size());
Packit ea1746
  for (int i = 0; i < num_parameters; ++i) {
Packit ea1746
    EXPECT_EQ(residual_size, results.local_jacobians.at(i).rows());
Packit ea1746
    EXPECT_EQ(local_parameter_sizes[i], results.local_jacobians.at(i).cols());
Packit ea1746
    EXPECT_EQ(residual_size, results.local_numeric_jacobians.at(i).rows());
Packit ea1746
    EXPECT_EQ(local_parameter_sizes[i],
Packit ea1746
              results.local_numeric_jacobians.at(i).cols());
Packit ea1746
    EXPECT_EQ(residual_size, results.jacobians.at(i).rows());
Packit ea1746
    EXPECT_EQ(parameter_sizes[i], results.jacobians.at(i).cols());
Packit ea1746
    EXPECT_EQ(residual_size, results.numeric_jacobians.at(i).rows());
Packit ea1746
    EXPECT_EQ(parameter_sizes[i], results.numeric_jacobians.at(i).cols());
Packit ea1746
  }
Packit ea1746
}
Packit ea1746
Packit ea1746
TEST(GradientChecker, SmokeTest) {
Packit ea1746
  srand(5);
Packit ea1746
Packit ea1746
  // Test with 3 blocks of size 2, 3 and 4.
Packit ea1746
  int const num_parameters = 3;
Packit ea1746
  std::vector<int> parameter_sizes(3);
Packit ea1746
  parameter_sizes[0] = 2;
Packit ea1746
  parameter_sizes[1] = 3;
Packit ea1746
  parameter_sizes[2] = 4;
Packit ea1746
Packit ea1746
  // Make a random set of blocks.
Packit ea1746
  FixedArray<double*> parameters(num_parameters);
Packit ea1746
  for (int j = 0; j < num_parameters; ++j) {
Packit ea1746
    parameters[j] = new double[parameter_sizes[j]];
Packit ea1746
    for (int u = 0; u < parameter_sizes[j]; ++u) {
Packit ea1746
      parameters[j][u] = 2.0 * RandDouble() - 1.0;
Packit ea1746
    }
Packit ea1746
  }
Packit ea1746
Packit ea1746
  NumericDiffOptions numeric_diff_options;
Packit ea1746
  GradientChecker::ProbeResults results;
Packit ea1746
Packit ea1746
  // Test that Probe returns true for correct Jacobians.
Packit ea1746
  GoodTestTerm good_term(num_parameters, parameter_sizes.data());
Packit ea1746
  GradientChecker good_gradient_checker(&good_term, NULL, numeric_diff_options);
Packit ea1746
  EXPECT_TRUE(good_gradient_checker.Probe(parameters.get(), kTolerance, NULL));
Packit ea1746
  EXPECT_TRUE(
Packit ea1746
      good_gradient_checker.Probe(parameters.get(), kTolerance, &results))
Packit ea1746
      << results.error_log;
Packit ea1746
Packit ea1746
  // Check that results contain sensible data.
Packit ea1746
  ASSERT_EQ(results.return_value, true);
Packit ea1746
  ASSERT_EQ(results.residuals.size(), 1);
Packit ea1746
  CheckDimensions(results, parameter_sizes, parameter_sizes, 1);
Packit ea1746
  EXPECT_GE(results.maximum_relative_error, 0.0);
Packit ea1746
  EXPECT_TRUE(results.error_log.empty());
Packit ea1746
Packit ea1746
  // Test that if the cost function return false, Probe should return false.
Packit ea1746
  good_term.SetReturnValue(false);
Packit ea1746
  EXPECT_FALSE(good_gradient_checker.Probe(parameters.get(), kTolerance, NULL));
Packit ea1746
  EXPECT_FALSE(
Packit ea1746
      good_gradient_checker.Probe(parameters.get(), kTolerance, &results))
Packit ea1746
      << results.error_log;
Packit ea1746
Packit ea1746
  // Check that results contain sensible data.
Packit ea1746
  ASSERT_EQ(results.return_value, false);
Packit ea1746
  ASSERT_EQ(results.residuals.size(), 1);
Packit ea1746
  CheckDimensions(results, parameter_sizes, parameter_sizes, 1);
Packit ea1746
  for (int i = 0; i < num_parameters; ++i) {
Packit ea1746
    EXPECT_EQ(results.local_jacobians.at(i).norm(), 0);
Packit ea1746
    EXPECT_EQ(results.local_numeric_jacobians.at(i).norm(), 0);
Packit ea1746
  }
Packit ea1746
  EXPECT_EQ(results.maximum_relative_error, 0.0);
Packit ea1746
  EXPECT_FALSE(results.error_log.empty());
Packit ea1746
Packit ea1746
  // Test that Probe returns false for incorrect Jacobians.
Packit ea1746
  BadTestTerm bad_term(num_parameters, parameter_sizes.data());
Packit ea1746
  GradientChecker bad_gradient_checker(&bad_term, NULL, numeric_diff_options);
Packit ea1746
  EXPECT_FALSE(bad_gradient_checker.Probe(parameters.get(), kTolerance, NULL));
Packit ea1746
  EXPECT_FALSE(
Packit ea1746
      bad_gradient_checker.Probe(parameters.get(), kTolerance, &results));
Packit ea1746
Packit ea1746
  // Check that results contain sensible data.
Packit ea1746
  ASSERT_EQ(results.return_value, true);
Packit ea1746
  ASSERT_EQ(results.residuals.size(), 1);
Packit ea1746
  CheckDimensions(results, parameter_sizes, parameter_sizes, 1);
Packit ea1746
  EXPECT_GT(results.maximum_relative_error, kTolerance);
Packit ea1746
  EXPECT_FALSE(results.error_log.empty());
Packit ea1746
Packit ea1746
  // Setting a high threshold should make the test pass.
Packit ea1746
  EXPECT_TRUE(bad_gradient_checker.Probe(parameters.get(), 1.0, &results));
Packit ea1746
Packit ea1746
  // Check that results contain sensible data.
Packit ea1746
  ASSERT_EQ(results.return_value, true);
Packit ea1746
  ASSERT_EQ(results.residuals.size(), 1);
Packit ea1746
  CheckDimensions(results, parameter_sizes, parameter_sizes, 1);
Packit ea1746
  EXPECT_GT(results.maximum_relative_error, 0.0);
Packit ea1746
  EXPECT_TRUE(results.error_log.empty());
Packit ea1746
Packit ea1746
  for (int j = 0; j < num_parameters; j++) {
Packit ea1746
    delete[] parameters[j];
Packit ea1746
  }
Packit ea1746
}
Packit ea1746
Packit ea1746
/**
Packit ea1746
 * Helper cost function that multiplies the parameters by the given jacobians
Packit ea1746
 * and adds a constant offset.
Packit ea1746
 */
Packit ea1746
class LinearCostFunction : public CostFunction {
Packit ea1746
 public:
Packit ea1746
  explicit LinearCostFunction(const Vector& residuals_offset)
Packit ea1746
      : residuals_offset_(residuals_offset) {
Packit ea1746
    set_num_residuals(residuals_offset_.size());
Packit ea1746
  }
Packit ea1746
Packit ea1746
  virtual bool Evaluate(double const* const* parameter_ptrs,
Packit ea1746
                        double* residuals_ptr,
Packit ea1746
                        double** residual_J_params) const {
Packit ea1746
    CHECK_GE(residual_J_params_.size(), 0.0);
Packit ea1746
    VectorRef residuals(residuals_ptr, residual_J_params_[0].rows());
Packit ea1746
    residuals = residuals_offset_;
Packit ea1746
Packit ea1746
    for (size_t i = 0; i < residual_J_params_.size(); ++i) {
Packit ea1746
      const Matrix& residual_J_param = residual_J_params_[i];
Packit ea1746
      int parameter_size = residual_J_param.cols();
Packit ea1746
      ConstVectorRef param(parameter_ptrs[i], parameter_size);
Packit ea1746
Packit ea1746
      // Compute residual.
Packit ea1746
      residuals += residual_J_param * param;
Packit ea1746
Packit ea1746
      // Return Jacobian.
Packit ea1746
      if (residual_J_params != NULL && residual_J_params[i] != NULL) {
Packit ea1746
        Eigen::Map<Matrix> residual_J_param_out(residual_J_params[i],
Packit ea1746
                                                residual_J_param.rows(),
Packit ea1746
                                                residual_J_param.cols());
Packit ea1746
        if (jacobian_offsets_.count(i) != 0) {
Packit ea1746
          residual_J_param_out = residual_J_param + jacobian_offsets_.at(i);
Packit ea1746
        } else {
Packit ea1746
          residual_J_param_out = residual_J_param;
Packit ea1746
        }
Packit ea1746
      }
Packit ea1746
    }
Packit ea1746
    return true;
Packit ea1746
  }
Packit ea1746
Packit ea1746
  void AddParameter(const Matrix& residual_J_param) {
Packit ea1746
    CHECK_EQ(num_residuals(), residual_J_param.rows());
Packit ea1746
    residual_J_params_.push_back(residual_J_param);
Packit ea1746
    mutable_parameter_block_sizes()->push_back(residual_J_param.cols());
Packit ea1746
  }
Packit ea1746
Packit ea1746
  /// Add offset to the given Jacobian before returning it from Evaluate(),
Packit ea1746
  /// thus introducing an error in the comutation.
Packit ea1746
  void SetJacobianOffset(size_t index, Matrix offset) {
Packit ea1746
    CHECK_LT(index, residual_J_params_.size());
Packit ea1746
    CHECK_EQ(residual_J_params_[index].rows(), offset.rows());
Packit ea1746
    CHECK_EQ(residual_J_params_[index].cols(), offset.cols());
Packit ea1746
    jacobian_offsets_[index] = offset;
Packit ea1746
  }
Packit ea1746
Packit ea1746
 private:
Packit ea1746
  std::vector<Matrix> residual_J_params_;
Packit ea1746
  std::map<int, Matrix> jacobian_offsets_;
Packit ea1746
  Vector residuals_offset_;
Packit ea1746
};
Packit ea1746
Packit ea1746
/**
Packit ea1746
 * Helper local parameterization that multiplies the delta vector by the given
Packit ea1746
 * jacobian and adds it to the parameter.
Packit ea1746
 */
Packit ea1746
class MatrixParameterization : public LocalParameterization {
Packit ea1746
 public:
Packit ea1746
  virtual bool Plus(const double* x,
Packit ea1746
                    const double* delta,
Packit ea1746
                    double* x_plus_delta) const {
Packit ea1746
    VectorRef(x_plus_delta, GlobalSize()) =
Packit ea1746
        ConstVectorRef(x, GlobalSize()) +
Packit ea1746
        (global_J_local * ConstVectorRef(delta, LocalSize()));
Packit ea1746
    return true;
Packit ea1746
  }
Packit ea1746
Packit ea1746
  virtual bool ComputeJacobian(const double* /*x*/, double* jacobian) const {
Packit ea1746
    MatrixRef(jacobian, GlobalSize(), LocalSize()) = global_J_local;
Packit ea1746
    return true;
Packit ea1746
  }
Packit ea1746
Packit ea1746
  virtual int GlobalSize() const { return global_J_local.rows(); }
Packit ea1746
  virtual int LocalSize() const { return global_J_local.cols(); }
Packit ea1746
Packit ea1746
  Matrix global_J_local;
Packit ea1746
};
Packit ea1746
Packit ea1746
// Helper function to compare two Eigen matrices (used in the test below).
Packit ea1746
void ExpectMatricesClose(Matrix p, Matrix q, double tolerance) {
Packit ea1746
  ASSERT_EQ(p.rows(), q.rows());
Packit ea1746
  ASSERT_EQ(p.cols(), q.cols());
Packit ea1746
  ExpectArraysClose(p.size(), p.data(), q.data(), tolerance);
Packit ea1746
}
Packit ea1746
Packit ea1746
TEST(GradientChecker, TestCorrectnessWithLocalParameterizations) {
Packit ea1746
  // Create cost function.
Packit ea1746
  Eigen::Vector3d residual_offset(100.0, 200.0, 300.0);
Packit ea1746
  LinearCostFunction cost_function(residual_offset);
Packit ea1746
  Eigen::Matrix<double, 3, 3, Eigen::RowMajor> j0;
Packit ea1746
  j0.row(0) << 1.0, 2.0, 3.0;
Packit ea1746
  j0.row(1) << 4.0, 5.0, 6.0;
Packit ea1746
  j0.row(2) << 7.0, 8.0, 9.0;
Packit ea1746
  Eigen::Matrix<double, 3, 2, Eigen::RowMajor> j1;
Packit ea1746
  j1.row(0) << 10.0, 11.0;
Packit ea1746
  j1.row(1) << 12.0, 13.0;
Packit ea1746
  j1.row(2) << 14.0, 15.0;
Packit ea1746
Packit ea1746
  Eigen::Vector3d param0(1.0, 2.0, 3.0);
Packit ea1746
  Eigen::Vector2d param1(4.0, 5.0);
Packit ea1746
Packit ea1746
  cost_function.AddParameter(j0);
Packit ea1746
  cost_function.AddParameter(j1);
Packit ea1746
Packit ea1746
  std::vector<int> parameter_sizes(2);
Packit ea1746
  parameter_sizes[0] = 3;
Packit ea1746
  parameter_sizes[1] = 2;
Packit ea1746
  std::vector<int> local_parameter_sizes(2);
Packit ea1746
  local_parameter_sizes[0] = 2;
Packit ea1746
  local_parameter_sizes[1] = 2;
Packit ea1746
Packit ea1746
  // Test cost function for correctness.
Packit ea1746
  Eigen::Matrix<double, 3, 3, Eigen::RowMajor> j1_out;
Packit ea1746
  Eigen::Matrix<double, 3, 2, Eigen::RowMajor> j2_out;
Packit ea1746
  Eigen::Vector3d residual;
Packit ea1746
  std::vector<const double*> parameters(2);
Packit ea1746
  parameters[0] = param0.data();
Packit ea1746
  parameters[1] = param1.data();
Packit ea1746
  std::vector<double*> jacobians(2);
Packit ea1746
  jacobians[0] = j1_out.data();
Packit ea1746
  jacobians[1] = j2_out.data();
Packit ea1746
  cost_function.Evaluate(parameters.data(), residual.data(), jacobians.data());
Packit ea1746
Packit ea1746
  Matrix residual_expected = residual_offset + j0 * param0 + j1 * param1;
Packit ea1746
Packit ea1746
  ExpectMatricesClose(j1_out, j0, std::numeric_limits<double>::epsilon());
Packit ea1746
  ExpectMatricesClose(j2_out, j1, std::numeric_limits<double>::epsilon());
Packit ea1746
  ExpectMatricesClose(residual, residual_expected, kTolerance);
Packit ea1746
Packit ea1746
  // Create local parameterization.
Packit ea1746
  Eigen::Matrix<double, 3, 2, Eigen::RowMajor> global_J_local;
Packit ea1746
  global_J_local.row(0) << 1.5, 2.5;
Packit ea1746
  global_J_local.row(1) << 3.5, 4.5;
Packit ea1746
  global_J_local.row(2) << 5.5, 6.5;
Packit ea1746
Packit ea1746
  MatrixParameterization parameterization;
Packit ea1746
  parameterization.global_J_local = global_J_local;
Packit ea1746
Packit ea1746
  // Test local parameterization for correctness.
Packit ea1746
  Eigen::Vector3d x(7.0, 8.0, 9.0);
Packit ea1746
  Eigen::Vector2d delta(10.0, 11.0);
Packit ea1746
Packit ea1746
  Eigen::Matrix<double, 3, 2, Eigen::RowMajor> global_J_local_out;
Packit ea1746
  parameterization.ComputeJacobian(x.data(), global_J_local_out.data());
Packit ea1746
  ExpectMatricesClose(global_J_local_out,
Packit ea1746
                      global_J_local,
Packit ea1746
                      std::numeric_limits<double>::epsilon());
Packit ea1746
Packit ea1746
  Eigen::Vector3d x_plus_delta;
Packit ea1746
  parameterization.Plus(x.data(), delta.data(), x_plus_delta.data());
Packit ea1746
  Eigen::Vector3d x_plus_delta_expected = x + (global_J_local * delta);
Packit ea1746
  ExpectMatricesClose(x_plus_delta, x_plus_delta_expected, kTolerance);
Packit ea1746
Packit ea1746
  // Now test GradientChecker.
Packit ea1746
  std::vector<const LocalParameterization*> parameterizations(2);
Packit ea1746
  parameterizations[0] = &parameterization;
Packit ea1746
  parameterizations[1] = NULL;
Packit ea1746
  NumericDiffOptions numeric_diff_options;
Packit ea1746
  GradientChecker::ProbeResults results;
Packit ea1746
  GradientChecker gradient_checker(
Packit ea1746
      &cost_function, &parameterizations, numeric_diff_options);
Packit ea1746
Packit ea1746
  Problem::Options problem_options;
Packit ea1746
  problem_options.cost_function_ownership = DO_NOT_TAKE_OWNERSHIP;
Packit ea1746
  problem_options.local_parameterization_ownership = DO_NOT_TAKE_OWNERSHIP;
Packit ea1746
  Problem problem(problem_options);
Packit ea1746
  Eigen::Vector3d param0_solver;
Packit ea1746
  Eigen::Vector2d param1_solver;
Packit ea1746
  problem.AddParameterBlock(param0_solver.data(), 3, &parameterization);
Packit ea1746
  problem.AddParameterBlock(param1_solver.data(), 2);
Packit ea1746
  problem.AddResidualBlock(
Packit ea1746
      &cost_function, NULL, param0_solver.data(), param1_solver.data());
Packit ea1746
  Solver::Options solver_options;
Packit ea1746
  solver_options.check_gradients = true;
Packit ea1746
  solver_options.initial_trust_region_radius = 1e10;
Packit ea1746
  Solver solver;
Packit ea1746
  Solver::Summary summary;
Packit ea1746
Packit ea1746
  // First test case: everything is correct.
Packit ea1746
  EXPECT_TRUE(gradient_checker.Probe(parameters.data(), kTolerance, NULL));
Packit ea1746
  EXPECT_TRUE(gradient_checker.Probe(parameters.data(), kTolerance, &results))
Packit ea1746
      << results.error_log;
Packit ea1746
Packit ea1746
  // Check that results contain correct data.
Packit ea1746
  ASSERT_EQ(results.return_value, true);
Packit ea1746
  ExpectMatricesClose(
Packit ea1746
      results.residuals, residual, std::numeric_limits<double>::epsilon());
Packit ea1746
  CheckDimensions(results, parameter_sizes, local_parameter_sizes, 3);
Packit ea1746
  ExpectMatricesClose(
Packit ea1746
      results.local_jacobians.at(0), j0 * global_J_local, kTolerance);
Packit ea1746
  ExpectMatricesClose(results.local_jacobians.at(1),
Packit ea1746
                      j1,
Packit ea1746
                      std::numeric_limits<double>::epsilon());
Packit ea1746
  ExpectMatricesClose(
Packit ea1746
      results.local_numeric_jacobians.at(0), j0 * global_J_local, kTolerance);
Packit ea1746
  ExpectMatricesClose(results.local_numeric_jacobians.at(1), j1, kTolerance);
Packit ea1746
  ExpectMatricesClose(
Packit ea1746
      results.jacobians.at(0), j0, std::numeric_limits<double>::epsilon());
Packit ea1746
  ExpectMatricesClose(
Packit ea1746
      results.jacobians.at(1), j1, std::numeric_limits<double>::epsilon());
Packit ea1746
  ExpectMatricesClose(results.numeric_jacobians.at(0), j0, kTolerance);
Packit ea1746
  ExpectMatricesClose(results.numeric_jacobians.at(1), j1, kTolerance);
Packit ea1746
  EXPECT_GE(results.maximum_relative_error, 0.0);
Packit ea1746
  EXPECT_TRUE(results.error_log.empty());
Packit ea1746
Packit ea1746
  // Test interaction with the 'check_gradients' option in Solver.
Packit ea1746
  param0_solver = param0;
Packit ea1746
  param1_solver = param1;
Packit ea1746
  solver.Solve(solver_options, &problem, &summary);
Packit ea1746
  EXPECT_EQ(CONVERGENCE, summary.termination_type);
Packit ea1746
  EXPECT_LE(summary.final_cost, 1e-12);
Packit ea1746
Packit ea1746
  // Second test case: Mess up reported derivatives with respect to 3rd
Packit ea1746
  // component of 1st parameter. Check should fail.
Packit ea1746
  Eigen::Matrix<double, 3, 3, Eigen::RowMajor> j0_offset;
Packit ea1746
  j0_offset.setZero();
Packit ea1746
  j0_offset.col(2).setConstant(0.001);
Packit ea1746
  cost_function.SetJacobianOffset(0, j0_offset);
Packit ea1746
  EXPECT_FALSE(gradient_checker.Probe(parameters.data(), kTolerance, NULL));
Packit ea1746
  EXPECT_FALSE(gradient_checker.Probe(parameters.data(), kTolerance, &results))
Packit ea1746
      << results.error_log;
Packit ea1746
Packit ea1746
  // Check that results contain correct data.
Packit ea1746
  ASSERT_EQ(results.return_value, true);
Packit ea1746
  ExpectMatricesClose(
Packit ea1746
      results.residuals, residual, std::numeric_limits<double>::epsilon());
Packit ea1746
  CheckDimensions(results, parameter_sizes, local_parameter_sizes, 3);
Packit ea1746
  ASSERT_EQ(results.local_jacobians.size(), 2);
Packit ea1746
  ASSERT_EQ(results.local_numeric_jacobians.size(), 2);
Packit ea1746
  ExpectMatricesClose(results.local_jacobians.at(0),
Packit ea1746
                      (j0 + j0_offset) * global_J_local,
Packit ea1746
                      kTolerance);
Packit ea1746
  ExpectMatricesClose(results.local_jacobians.at(1),
Packit ea1746
                      j1,
Packit ea1746
                      std::numeric_limits<double>::epsilon());
Packit ea1746
  ExpectMatricesClose(
Packit ea1746
      results.local_numeric_jacobians.at(0), j0 * global_J_local, kTolerance);
Packit ea1746
  ExpectMatricesClose(results.local_numeric_jacobians.at(1), j1, kTolerance);
Packit ea1746
  ExpectMatricesClose(results.jacobians.at(0), j0 + j0_offset, kTolerance);
Packit ea1746
  ExpectMatricesClose(
Packit ea1746
      results.jacobians.at(1), j1, std::numeric_limits<double>::epsilon());
Packit ea1746
  ExpectMatricesClose(results.numeric_jacobians.at(0), j0, kTolerance);
Packit ea1746
  ExpectMatricesClose(results.numeric_jacobians.at(1), j1, kTolerance);
Packit ea1746
  EXPECT_GT(results.maximum_relative_error, 0.0);
Packit ea1746
  EXPECT_FALSE(results.error_log.empty());
Packit ea1746
Packit ea1746
  // Test interaction with the 'check_gradients' option in Solver.
Packit ea1746
  param0_solver = param0;
Packit ea1746
  param1_solver = param1;
Packit ea1746
  solver.Solve(solver_options, &problem, &summary);
Packit ea1746
  EXPECT_EQ(FAILURE, summary.termination_type);
Packit ea1746
Packit ea1746
  // Now, zero out the local parameterization Jacobian of the 1st parameter
Packit ea1746
  // with respect to the 3rd component. This makes the combination of
Packit ea1746
  // cost function and local parameterization return correct values again.
Packit ea1746
  parameterization.global_J_local.row(2).setZero();
Packit ea1746
Packit ea1746
  // Verify that the gradient checker does not treat this as an error.
Packit ea1746
  EXPECT_TRUE(gradient_checker.Probe(parameters.data(), kTolerance, &results))
Packit ea1746
      << results.error_log;
Packit ea1746
Packit ea1746
  // Check that results contain correct data.
Packit ea1746
  ASSERT_EQ(results.return_value, true);
Packit ea1746
  ExpectMatricesClose(
Packit ea1746
      results.residuals, residual, std::numeric_limits<double>::epsilon());
Packit ea1746
  CheckDimensions(results, parameter_sizes, local_parameter_sizes, 3);
Packit ea1746
  ASSERT_EQ(results.local_jacobians.size(), 2);
Packit ea1746
  ASSERT_EQ(results.local_numeric_jacobians.size(), 2);
Packit ea1746
  ExpectMatricesClose(results.local_jacobians.at(0),
Packit ea1746
                      (j0 + j0_offset) * parameterization.global_J_local,
Packit ea1746
                      kTolerance);
Packit ea1746
  ExpectMatricesClose(results.local_jacobians.at(1),
Packit ea1746
                      j1,
Packit ea1746
                      std::numeric_limits<double>::epsilon());
Packit ea1746
  ExpectMatricesClose(results.local_numeric_jacobians.at(0),
Packit ea1746
                      j0 * parameterization.global_J_local,
Packit ea1746
                      kTolerance);
Packit ea1746
  ExpectMatricesClose(results.local_numeric_jacobians.at(1), j1, kTolerance);
Packit ea1746
  ExpectMatricesClose(results.jacobians.at(0), j0 + j0_offset, kTolerance);
Packit ea1746
  ExpectMatricesClose(
Packit ea1746
      results.jacobians.at(1), j1, std::numeric_limits<double>::epsilon());
Packit ea1746
  ExpectMatricesClose(results.numeric_jacobians.at(0), j0, kTolerance);
Packit ea1746
  ExpectMatricesClose(results.numeric_jacobians.at(1), j1, kTolerance);
Packit ea1746
  EXPECT_GE(results.maximum_relative_error, 0.0);
Packit ea1746
  EXPECT_TRUE(results.error_log.empty());
Packit ea1746
Packit ea1746
  // Test interaction with the 'check_gradients' option in Solver.
Packit ea1746
  param0_solver = param0;
Packit ea1746
  param1_solver = param1;
Packit ea1746
  solver.Solve(solver_options, &problem, &summary);
Packit ea1746
  EXPECT_EQ(CONVERGENCE, summary.termination_type);
Packit ea1746
  EXPECT_LE(summary.final_cost, 1e-12);
Packit ea1746
}
Packit ea1746
Packit ea1746
}  // namespace internal
Packit ea1746
}  // namespace ceres