// 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. // Copyright 2007 Google Inc. All Rights Reserved. // // Author: wjr@google.com (William Rucklidge) // // This file contains a class that exercises a cost function, to make sure // that it is computing reasonable derivatives. It compares the Jacobians // computed by the cost function with those obtained by finite // differences. #ifndef CERES_PUBLIC_GRADIENT_CHECKER_H_ #define CERES_PUBLIC_GRADIENT_CHECKER_H_ #include #include #include #include "ceres/internal/eigen.h" #include "ceres/internal/fixed_array.h" #include "ceres/internal/macros.h" #include "ceres/internal/scoped_ptr.h" #include "ceres/numeric_diff_cost_function.h" #include "glog/logging.h" namespace ceres { // An object that exercises a cost function, to compare the answers that it // gives with derivatives estimated using finite differencing. // // The only likely usage of this is for testing. // // How to use: Fill in an array of pointers to parameter blocks for your // CostFunction, and then call Probe(). Check that the return value is // 'true'. See prober_test.cc for an example. // // This is templated similarly to NumericDiffCostFunction, as it internally // uses that. template class GradientChecker { public: // Here we stash some results from the probe, for later // inspection. struct GradientCheckResults { // Computed cost. Vector cost; // The sizes of these matrices are dictated by the cost function's // parameter and residual block sizes. Each vector's length will // term->parameter_block_sizes().size(), and each matrix is the // Jacobian of the residual with respect to the corresponding parameter // block. // Derivatives as computed by the cost function. vector term_jacobians; // Derivatives as computed by finite differencing. vector finite_difference_jacobians; // Infinity-norm of term_jacobians - finite_difference_jacobians. double error_jacobians; }; // Checks the Jacobian computed by a cost function. // // probe_point: The parameter values at which to probe. // error_tolerance: A threshold for the infinity-norm difference // between the Jacobians. If the Jacobians differ by more than // this amount, then the probe fails. // // term: The cost function to test. Not retained after this call returns. // // results: On return, the two Jacobians (and other information) // will be stored here. May be NULL. // // Returns true if no problems are detected and the difference between the // Jacobians is less than error_tolerance. static bool Probe(double const* const* probe_point, double error_tolerance, CostFunctionToProbe *term, GradientCheckResults* results) { CHECK_NOTNULL(probe_point); CHECK_NOTNULL(term); LOG(INFO) << "-------------------- Starting Probe() --------------------"; // We need a GradientCheckeresults, whether or not they supplied one. internal::scoped_ptr owned_results; if (results == NULL) { owned_results.reset(new GradientCheckResults); results = owned_results.get(); } // Do a consistency check between the term and the template parameters. CHECK_EQ(M, term->num_residuals()); const int num_residuals = M; const vector& block_sizes = term->parameter_block_sizes(); const int num_blocks = block_sizes.size(); CHECK_LE(num_blocks, 5) << "Unable to test functions that take more " << "than 5 parameter blocks"; if (N0) { CHECK_EQ(N0, block_sizes[0]); CHECK_GE(num_blocks, 1); } else { CHECK_LT(num_blocks, 1); } if (N1) { CHECK_EQ(N1, block_sizes[1]); CHECK_GE(num_blocks, 2); } else { CHECK_LT(num_blocks, 2); } if (N2) { CHECK_EQ(N2, block_sizes[2]); CHECK_GE(num_blocks, 3); } else { CHECK_LT(num_blocks, 3); } if (N3) { CHECK_EQ(N3, block_sizes[3]); CHECK_GE(num_blocks, 4); } else { CHECK_LT(num_blocks, 4); } if (N4) { CHECK_EQ(N4, block_sizes[4]); CHECK_GE(num_blocks, 5); } else { CHECK_LT(num_blocks, 5); } results->term_jacobians.clear(); results->term_jacobians.resize(num_blocks); results->finite_difference_jacobians.clear(); results->finite_difference_jacobians.resize(num_blocks); internal::FixedArray term_jacobian_pointers(num_blocks); internal::FixedArray finite_difference_jacobian_pointers(num_blocks); for (int i = 0; i < num_blocks; i++) { results->term_jacobians[i].resize(num_residuals, block_sizes[i]); term_jacobian_pointers[i] = results->term_jacobians[i].data(); results->finite_difference_jacobians[i].resize( num_residuals, block_sizes[i]); finite_difference_jacobian_pointers[i] = results->finite_difference_jacobians[i].data(); } results->cost.resize(num_residuals, 1); CHECK(term->Evaluate(probe_point, results->cost.data(), term_jacobian_pointers.get())); NumericDiffCostFunction numeric_term(term, DO_NOT_TAKE_OWNERSHIP); CHECK(numeric_term.Evaluate(probe_point, results->cost.data(), finite_difference_jacobian_pointers.get())); results->error_jacobians = 0; for (int i = 0; i < num_blocks; i++) { Matrix jacobian_difference = results->term_jacobians[i] - results->finite_difference_jacobians[i]; results->error_jacobians = std::max(results->error_jacobians, jacobian_difference.lpNorm()); } LOG(INFO) << "========== term-computed derivatives =========="; for (int i = 0; i < num_blocks; i++) { LOG(INFO) << "term_computed block " << i; LOG(INFO) << "\n" << results->term_jacobians[i]; } LOG(INFO) << "========== finite-difference derivatives =========="; for (int i = 0; i < num_blocks; i++) { LOG(INFO) << "finite_difference block " << i; LOG(INFO) << "\n" << results->finite_difference_jacobians[i]; } LOG(INFO) << "========== difference =========="; for (int i = 0; i < num_blocks; i++) { LOG(INFO) << "difference block " << i; LOG(INFO) << (results->term_jacobians[i] - results->finite_difference_jacobians[i]); } LOG(INFO) << "||difference|| = " << results->error_jacobians; return results->error_jacobians < error_tolerance; } private: CERES_DISALLOW_IMPLICIT_CONSTRUCTORS(GradientChecker); }; } // namespace ceres #endif // CERES_PUBLIC_GRADIENT_CHECKER_H_