Evaluate ResidualBlocks without LossFunction if needed. 1. Add the ability to evaluate the problem without loss function. 2. Remove static Evaluator::Evaluate 3. Refactor the common code from problem_test.cc and evaluator_test.cc into evaluator_test_utils.cc Change-Id: I1aa841580afe91d288fbb65288b0ffdd1e43e827
diff --git a/include/ceres/problem.h b/include/ceres/problem.h index bab3bfe..b1ccbab 100644 --- a/include/ceres/problem.h +++ b/include/ceres/problem.h
@@ -331,7 +331,8 @@ // Options struct to control Problem::Evaluate. struct EvaluateOptions { EvaluateOptions() - : num_threads(1) { + : apply_loss_function(true), + num_threads(1) { } // The set of parameter blocks for which evaluation should be @@ -360,6 +361,15 @@ // they were added to the problem. But, this may change if the // user removes any residual blocks from the problem. vector<ResidualBlockId> residual_blocks; + + // Even though the residual blocks in the problem may contain loss + // functions, setting apply_loss_function to false will turn off + // the application of the loss function to the output of the cost + // function. This is of use for example if the user wishes to + // analyse the solution quality by studying the distribution of + // residuals before and after the solve. + bool apply_loss_function; + int num_threads; };
diff --git a/internal/ceres/CMakeLists.txt b/internal/ceres/CMakeLists.txt index 5440713..703a9e7 100644 --- a/internal/ceres/CMakeLists.txt +++ b/internal/ceres/CMakeLists.txt
@@ -215,7 +215,11 @@ IF (${BUILD_TESTING} AND ${GFLAGS}) ADD_LIBRARY(gtest gmock_gtest_all.cc gmock_main.cc) - ADD_LIBRARY(test_util test_util.cc numeric_diff_test_utils.cc) + ADD_LIBRARY(test_util + evaluator_test_utils.cc + numeric_diff_test_utils.cc + test_util.cc) + TARGET_LINK_LIBRARIES(gtest ${GFLAGS_LIB} ${GLOG_LIB}) MACRO (CERES_TEST NAME)
diff --git a/internal/ceres/evaluator.cc b/internal/ceres/evaluator.cc index a3ce6f0..31a4176 100644 --- a/internal/ceres/evaluator.cc +++ b/internal/ceres/evaluator.cc
@@ -72,76 +72,5 @@ } } -bool Evaluator::Evaluate(Program* program, - int num_threads, - double* cost, - vector<double>* residuals, - vector<double>* gradient, - CRSMatrix* output_jacobian) { - CHECK_GE(num_threads, 1) - << "This is a Ceres bug; please contact the developers!"; - CHECK_NOTNULL(cost); - - // Setup the Parameter indices and offsets before an evaluator can - // be constructed and used. - program->SetParameterOffsetsAndIndex(); - - Evaluator::Options evaluator_options; - evaluator_options.linear_solver_type = SPARSE_NORMAL_CHOLESKY; - evaluator_options.num_threads = num_threads; - - string error; - scoped_ptr<Evaluator> evaluator( - Evaluator::Create(evaluator_options, program, &error)); - if (evaluator.get() == NULL) { - LOG(ERROR) << "Unable to create an Evaluator object. " - << "Error: " << error - << "This is a Ceres bug; please contact the developers!"; - return false; - } - - if (residuals !=NULL) { - residuals->resize(evaluator->NumResiduals()); - } - - if (gradient != NULL) { - gradient->resize(evaluator->NumEffectiveParameters()); - } - - scoped_ptr<CompressedRowSparseMatrix> jacobian; - if (output_jacobian != NULL) { - jacobian.reset( - down_cast<CompressedRowSparseMatrix*>(evaluator->CreateJacobian())); - } - - // Point the state pointers to the user state pointers. This is - // needed so that we can extract a parameter vector which is then - // passed to Evaluator::Evaluate. - program->SetParameterBlockStatePtrsToUserStatePtrs(); - - // Copy the value of the parameter blocks into a vector, since the - // Evaluate::Evaluate method needs its input as such. The previous - // call to SetParameterBlockStatePtrsToUserStatePtrs ensures that - // these values are the ones corresponding to the actual state of - // the parameter blocks, rather than the temporary state pointer - // used for evaluation. - Vector parameters(program->NumParameters()); - program->ParameterBlocksToStateVector(parameters.data()); - - if (!evaluator->Evaluate(parameters.data(), - cost, - residuals != NULL ? &(*residuals)[0] : NULL, - gradient != NULL ? &(*gradient)[0] : NULL, - jacobian.get())) { - return false; - } - - if (output_jacobian != NULL) { - jacobian->ToCRSMatrix(output_jacobian); - } - - return true; -} - } // namespace internal } // namespace ceres
diff --git a/internal/ceres/evaluator.h b/internal/ceres/evaluator.h index 14a8818..07cfa37 100644 --- a/internal/ceres/evaluator.h +++ b/internal/ceres/evaluator.h
@@ -72,7 +72,6 @@ Program* program, string* error); - // This is used for computing the cost, residual and Jacobian for // returning to the user. For actually solving the optimization // problem, the optimization algorithm uses the ProgramEvaluator @@ -116,6 +115,18 @@ // Schur complement based methods. virtual SparseMatrix* CreateJacobian() const = 0; + + // Options struct to control Evaluator::Evaluate; + struct EvaluateOptions { + EvaluateOptions() + : apply_loss_function(true) { + } + + // If false, the loss function correction is not applied to the + // residual blocks. + bool apply_loss_function; + }; + // Evaluate the cost function for the given state. Returns the cost, // residuals, and jacobian in the corresponding arguments. Both residuals and // jacobian are optional; to avoid computing them, pass NULL. @@ -125,12 +136,29 @@ // // state is an array of size NumParameters(), cost is a pointer to a single // double, and residuals is an array of doubles of size NumResiduals(). - virtual bool Evaluate(const double* state, + virtual bool Evaluate(const EvaluateOptions& evaluate_options, + const double* state, double* cost, double* residuals, double* gradient, SparseMatrix* jacobian) = 0; + // Variant of Evaluator::Evaluate where the user wishes to use the + // default EvaluateOptions struct. This is mostly here as a + // convenience method. + virtual bool Evaluate(const double* state, + double* cost, + double* residuals, + double* gradient, + SparseMatrix* jacobian) { + return Evaluate(EvaluateOptions(), + state, + cost, + residuals, + gradient, + jacobian); + } + // Make a change delta (of size NumEffectiveParameters()) to state (of size // NumParameters()) and store the result in state_plus_delta. //
diff --git a/internal/ceres/evaluator_test.cc b/internal/ceres/evaluator_test.cc index 9e9d31b..ea24504 100644 --- a/internal/ceres/evaluator_test.cc +++ b/internal/ceres/evaluator_test.cc
@@ -36,6 +36,7 @@ #include "ceres/casts.h" #include "ceres/cost_function.h" #include "ceres/crs_matrix.h" +#include "ceres/evaluator_test_utils.h" #include "ceres/internal/eigen.h" #include "ceres/internal/scoped_ptr.h" #include "ceres/local_parameterization.h" @@ -90,65 +91,6 @@ } }; -struct ExpectedEvaluation { - int num_rows; - int num_cols; - double cost; - const double residuals[50]; - const double gradient[50]; - const double jacobian[200]; -}; - -void CompareEvaluations(int expected_num_rows, - int expected_num_cols, - double expected_cost, - const double* expected_residuals, - const double* expected_gradient, - const double* expected_jacobian, - const double actual_cost, - const double* actual_residuals, - const double* actual_gradient, - const double* actual_jacobian) { - EXPECT_EQ(expected_cost, actual_cost); - - if (expected_residuals != NULL) { - ConstVectorRef expected_residuals_vector(expected_residuals, - expected_num_rows); - ConstVectorRef actual_residuals_vector(actual_residuals, - expected_num_rows); - EXPECT_TRUE((actual_residuals_vector.array() == - expected_residuals_vector.array()).all()) - << "Actual:\n" << actual_residuals_vector - << "\nExpected:\n" << expected_residuals_vector; - } - - if (expected_gradient != NULL) { - ConstVectorRef expected_gradient_vector(expected_gradient, - expected_num_cols); - ConstVectorRef actual_gradient_vector(actual_gradient, - expected_num_cols); - - EXPECT_TRUE((actual_gradient_vector.array() == - expected_gradient_vector.array()).all()) - << "Actual:\n" << actual_gradient_vector.transpose() - << "\nExpected:\n" << expected_gradient_vector.transpose(); - } - - if (expected_jacobian != NULL) { - ConstMatrixRef expected_jacobian_matrix(expected_jacobian, - expected_num_rows, - expected_num_cols); - ConstMatrixRef actual_jacobian_matrix(actual_jacobian, - expected_num_rows, - expected_num_cols); - EXPECT_TRUE((actual_jacobian_matrix.array() == - expected_jacobian_matrix.array()).all()) - << "Actual:\n" << actual_jacobian_matrix - << "\nExpected:\n" << expected_jacobian_matrix; - } -} - - struct EvaluatorTest : public ::testing::TestWithParam<pair<LinearSolverType, int> > { Evaluator* CreateEvaluator(Program* program) { @@ -692,272 +634,5 @@ } } -// Simple cost function used for testing Evaluator::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 StaticEvaluateTest : public ::testing::Test { - protected: - void SetUp() { - for (int i = 0; i < 6; ++i) { - parameters_[i] = static_cast<double>(i + 1); - } - - CostFunction* cost_function = new QuadraticCostFunction<2, 2>; - - // f(x, y) - problem_.AddResidualBlock(cost_function, - NULL, - parameters_, - parameters_ + 2); - // g(y, z) - problem_.AddResidualBlock(cost_function, - NULL, parameters_ + 2, - parameters_ + 4); - // h(z, x) - problem_.AddResidualBlock(cost_function, - NULL, - parameters_ + 4, - parameters_); - } - - - - void EvaluateAndCompare(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(Evaluator::Evaluate( - problem_.mutable_program(), - 1, - &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 ExpectedEvaluation& expected ) { - for (int i = 0; i < 8; ++i) { - EvaluateAndCompare(expected.num_rows, - expected.num_cols, - expected.cost, - (i & 1) ? expected.residuals : NULL, - (i & 2) ? expected.gradient : NULL, - (i & 4) ? expected.jacobian : NULL); - } - - // The Evaluate call should only depend on the parameter block - // values in the user provided pointers, and the current state of - // the parameter block should not matter. So, create a new - // parameters vector, and update the parameter block states with - // it. The results from the Evaluate call should not change. - double new_parameters[6]; - for (int i = 0; i < 6; ++i) { - new_parameters[i] = 0.0; - } - - problem_.mutable_program()->StateVectorToParameterBlocks(new_parameters); - - for (int i = 0; i < 8; ++i) { - EvaluateAndCompare(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]; -}; - - -TEST_F(StaticEvaluateTest, 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(expected); -} - -TEST_F(StaticEvaluateTest, 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(expected); -} - -TEST_F(StaticEvaluateTest, 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(expected); -} - } // namespace internal } // namespace ceres
diff --git a/internal/ceres/evaluator_test_utils.cc b/internal/ceres/evaluator_test_utils.cc new file mode 100644 index 0000000..a09be2d --- /dev/null +++ b/internal/ceres/evaluator_test_utils.cc
@@ -0,0 +1,89 @@ +// Ceres Solver - A fast non-linear least squares minimizer +// Copyright 2013 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: keir@google.com (Keir Mierle) +// sameeragarwal@google.com (Sameer Agarwal) + +#include "ceres/evaluator_test_utils.h" +#include "ceres/internal/eigen.h" +#include "gtest/gtest.h" + +namespace ceres { +namespace internal { + +void CompareEvaluations(int expected_num_rows, + int expected_num_cols, + double expected_cost, + const double* expected_residuals, + const double* expected_gradient, + const double* expected_jacobian, + const double actual_cost, + const double* actual_residuals, + const double* actual_gradient, + const double* actual_jacobian) { + EXPECT_EQ(expected_cost, actual_cost); + + if (expected_residuals != NULL) { + ConstVectorRef expected_residuals_vector(expected_residuals, + expected_num_rows); + ConstVectorRef actual_residuals_vector(actual_residuals, + expected_num_rows); + EXPECT_TRUE((actual_residuals_vector.array() == + expected_residuals_vector.array()).all()) + << "Actual:\n" << actual_residuals_vector + << "\nExpected:\n" << expected_residuals_vector; + } + + if (expected_gradient != NULL) { + ConstVectorRef expected_gradient_vector(expected_gradient, + expected_num_cols); + ConstVectorRef actual_gradient_vector(actual_gradient, + expected_num_cols); + + EXPECT_TRUE((actual_gradient_vector.array() == + expected_gradient_vector.array()).all()) + << "Actual:\n" << actual_gradient_vector.transpose() + << "\nExpected:\n" << expected_gradient_vector.transpose(); + } + + if (expected_jacobian != NULL) { + ConstMatrixRef expected_jacobian_matrix(expected_jacobian, + expected_num_rows, + expected_num_cols); + ConstMatrixRef actual_jacobian_matrix(actual_jacobian, + expected_num_rows, + expected_num_cols); + EXPECT_TRUE((actual_jacobian_matrix.array() == + expected_jacobian_matrix.array()).all()) + << "Actual:\n" << actual_jacobian_matrix + << "\nExpected:\n" << expected_jacobian_matrix; + } +} + +} // namespace internal +} // namespace ceres
diff --git a/internal/ceres/evaluator_test_utils.h b/internal/ceres/evaluator_test_utils.h new file mode 100644 index 0000000..ae0663a --- /dev/null +++ b/internal/ceres/evaluator_test_utils.h
@@ -0,0 +1,60 @@ +// Ceres Solver - A fast non-linear least squares minimizer +// Copyright 2013 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: keir@google.com (Keir Mierle) +// sameeragarwal@google.com (Sameer Agarwal) +// +// Test utils used for evaluation testing. + +namespace ceres { +namespace internal { + +// Fixed sized struct for storing an evaluation. +struct ExpectedEvaluation { + int num_rows; + int num_cols; + double cost; + const double residuals[50]; + const double gradient[50]; + const double jacobian[200]; +}; + +// Compare two evaluations. +void CompareEvaluations(int expected_num_rows, + int expected_num_cols, + double expected_cost, + const double* expected_residuals, + const double* expected_gradient, + const double* expected_jacobian, + const double actual_cost, + const double* actual_residuals, + const double* actual_gradient, + const double* actual_jacobian); + +} // namespace internal +} // namespace ceres
diff --git a/internal/ceres/problem_impl.cc b/internal/ceres/problem_impl.cc index 1fb9e39..21d1144 100644 --- a/internal/ceres/problem_impl.cc +++ b/internal/ceres/problem_impl.cc
@@ -651,7 +651,12 @@ program.ParameterBlocksToStateVector(parameters.data()); double tmp_cost = 0; - bool status = evaluator->Evaluate(parameters.data(), + + Evaluator::EvaluateOptions evaluator_evaluate_options; + evaluator_evaluate_options.apply_loss_function = + evaluate_options.apply_loss_function; + bool status = evaluator->Evaluate(evaluator_evaluate_options, + parameters.data(), &tmp_cost, residuals != NULL ? &(*residuals)[0] : NULL, gradient != NULL ? &(*gradient)[0] : NULL,
diff --git a/internal/ceres/problem_test.cc b/internal/ceres/problem_test.cc index 888eb7c..5f3bc94 100644 --- a/internal/ceres/problem_test.cc +++ b/internal/ceres/problem_test.cc
@@ -35,6 +35,7 @@ #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" @@ -46,7 +47,6 @@ #include "ceres/types.h" #include "gtest/gtest.h" - namespace ceres { namespace internal { @@ -703,71 +703,6 @@ // Test for Problem::Evaluate -// TODO(sameeragarwal): The following struct and function are shared -// with evaluator_test.cc. Once things settle down, do an -// evaluate_utils.h or some such thing to reduce code duplication. The -// best time is perhaps when we remove the support for -// Solver::Summary::initial_* -struct ExpectedEvaluation { - int num_rows; - int num_cols; - double cost; - const double residuals[50]; - const double gradient[50]; - const double jacobian[200]; -}; - -void CompareEvaluations(int expected_num_rows, - int expected_num_cols, - double expected_cost, - const double* expected_residuals, - const double* expected_gradient, - const double* expected_jacobian, - const double actual_cost, - const double* actual_residuals, - const double* actual_gradient, - const double* actual_jacobian) { - EXPECT_EQ(expected_cost, actual_cost); - - if (expected_residuals != NULL) { - ConstVectorRef expected_residuals_vector(expected_residuals, - expected_num_rows); - ConstVectorRef actual_residuals_vector(actual_residuals, - expected_num_rows); - EXPECT_TRUE((actual_residuals_vector.array() == - expected_residuals_vector.array()).all()) - << "Actual:\n" << actual_residuals_vector - << "\nExpected:\n" << expected_residuals_vector; - } - - if (expected_gradient != NULL) { - ConstVectorRef expected_gradient_vector(expected_gradient, - expected_num_cols); - ConstVectorRef actual_gradient_vector(actual_gradient, - expected_num_cols); - - EXPECT_TRUE((actual_gradient_vector.array() == - expected_gradient_vector.array()).all()) - << "Actual:\n" << actual_gradient_vector.transpose() - << "\nExpected:\n" << expected_gradient_vector.transpose(); - } - - if (expected_jacobian != NULL) { - ConstMatrixRef expected_jacobian_matrix(expected_jacobian, - expected_num_rows, - expected_num_cols); - ConstMatrixRef actual_jacobian_matrix(actual_jacobian, - expected_num_rows, - expected_num_cols); - EXPECT_TRUE((actual_jacobian_matrix.array() == - expected_jacobian_matrix.array()).all()) - << "Actual:\n" << actual_jacobian_matrix - << "\nExpected:\n" << expected_jacobian_matrix; - } -} - -// Simple cost function used for testing Problem::Evaluate. -// // r_i = i - (j + 1) * x_ij^2 template <int kNumResiduals, int kNumParameterBlocks> class QuadraticCostFunction : public CostFunction {
diff --git a/internal/ceres/program_evaluator.h b/internal/ceres/program_evaluator.h index a19cdf8..de56ac2 100644 --- a/internal/ceres/program_evaluator.h +++ b/internal/ceres/program_evaluator.h
@@ -120,7 +120,8 @@ return jacobian_writer_.CreateJacobian(); } - bool Evaluate(const double* state, + bool Evaluate(const Evaluator::EvaluateOptions& evaluate_options, + const double* state, double* cost, double* residuals, double* gradient, @@ -196,6 +197,7 @@ // Evaluate the cost, residuals, and jacobians. double block_cost; if (!residual_block->Evaluate( + evaluate_options.apply_loss_function, &block_cost, block_residuals, block_jacobians,
diff --git a/internal/ceres/residual_block.cc b/internal/ceres/residual_block.cc index 7f78960..b91b0ed 100644 --- a/internal/ceres/residual_block.cc +++ b/internal/ceres/residual_block.cc
@@ -62,7 +62,8 @@ parameter_blocks_.get()); } -bool ResidualBlock::Evaluate(double* cost, +bool ResidualBlock::Evaluate(const bool apply_loss_function, + double* cost, double* residuals, double** jacobians, double* scratch) const { @@ -154,7 +155,7 @@ } } - if (loss_function_ == NULL) { + if (loss_function_ == NULL || !apply_loss_function) { *cost = 0.5 * squared_norm; return true; }
diff --git a/internal/ceres/residual_block.h b/internal/ceres/residual_block.h index 3921d1d..9c3671b 100644 --- a/internal/ceres/residual_block.h +++ b/internal/ceres/residual_block.h
@@ -93,11 +93,16 @@ // parameterizations applied already; for example, the jacobian for a // 4-dimensional quaternion parameter using the "QuaternionParameterization" // is num_residuals by 3 instead of num_residuals by 4. - bool Evaluate(double* cost, + // + // apply_loss_function as the name implies allows the user to switch + // the application of the loss function on and off. + bool Evaluate(bool apply_loss_function, + double* cost, double* residuals, double** jacobians, double* scratch) const; + const CostFunction* cost_function() const { return cost_function_; } const LossFunction* loss_function() const { return loss_function_; }
diff --git a/internal/ceres/residual_block_test.cc b/internal/ceres/residual_block_test.cc index fddd44e..1e03e7d 100644 --- a/internal/ceres/residual_block_test.cc +++ b/internal/ceres/residual_block_test.cc
@@ -105,12 +105,12 @@ // Verify cost-only evaluation. double cost; - residual_block.Evaluate(&cost, NULL, NULL, scratch); + residual_block.Evaluate(true, &cost, NULL, NULL, scratch); EXPECT_EQ(0.5 * (0*0 + 1*1 + 2*2), cost); // Verify cost and residual evaluation. double residuals[3]; - residual_block.Evaluate(&cost, residuals, NULL, scratch); + residual_block.Evaluate(true, &cost, residuals, NULL, scratch); EXPECT_EQ(0.5 * (0*0 + 1*1 + 2*2), cost); EXPECT_EQ(0.0, residuals[0]); EXPECT_EQ(1.0, residuals[1]); @@ -134,7 +134,7 @@ jacobian_rz.data() }; - residual_block.Evaluate(&cost, residuals, jacobian_ptrs, scratch); + residual_block.Evaluate(true, &cost, residuals, jacobian_ptrs, scratch); EXPECT_EQ(0.5 * (0*0 + 1*1 + 2*2), cost); EXPECT_EQ(0.0, residuals[0]); EXPECT_EQ(1.0, residuals[1]); @@ -153,7 +153,7 @@ jacobian_ptrs[1] = NULL; // Don't compute the jacobian for y. - residual_block.Evaluate(&cost, residuals, jacobian_ptrs, scratch); + residual_block.Evaluate(true, &cost, residuals, jacobian_ptrs, scratch); EXPECT_EQ(0.5 * (0*0 + 1*1 + 2*2), cost); EXPECT_EQ(0.0, residuals[0]); EXPECT_EQ(1.0, residuals[1]); @@ -244,12 +244,12 @@ // Verify cost-only evaluation. double cost; - residual_block.Evaluate(&cost, NULL, NULL, scratch); + residual_block.Evaluate(true, &cost, NULL, NULL, scratch); EXPECT_EQ(0.5 * (0*0 + 1*1 + 2*2), cost); // Verify cost and residual evaluation. double residuals[3]; - residual_block.Evaluate(&cost, residuals, NULL, scratch); + residual_block.Evaluate(true, &cost, residuals, NULL, scratch); EXPECT_EQ(0.5 * (0*0 + 1*1 + 2*2), cost); EXPECT_EQ(0.0, residuals[0]); EXPECT_EQ(1.0, residuals[1]); @@ -273,7 +273,7 @@ jacobian_rz.data() }; - residual_block.Evaluate(&cost, residuals, jacobian_ptrs, scratch); + residual_block.Evaluate(true, &cost, residuals, jacobian_ptrs, scratch); EXPECT_EQ(0.5 * (0*0 + 1*1 + 2*2), cost); EXPECT_EQ(0.0, residuals[0]); EXPECT_EQ(1.0, residuals[1]); @@ -311,7 +311,7 @@ jacobian_ptrs[1] = NULL; // Don't compute the jacobian for y. - residual_block.Evaluate(&cost, residuals, jacobian_ptrs, scratch); + residual_block.Evaluate(true, &cost, residuals, jacobian_ptrs, scratch); EXPECT_EQ(0.5 * (0*0 + 1*1 + 2*2), cost); EXPECT_EQ(0.0, residuals[0]); EXPECT_EQ(1.0, residuals[1]);
diff --git a/internal/ceres/residual_block_utils_test.cc b/internal/ceres/residual_block_utils_test.cc index 24723b3..d3c917a 100644 --- a/internal/ceres/residual_block_utils_test.cc +++ b/internal/ceres/residual_block_utils_test.cc
@@ -62,7 +62,8 @@ double jacobian; double* jacobians[] = { &jacobian }; - EXPECT_EQ(residual_block.Evaluate(&cost, + EXPECT_EQ(residual_block.Evaluate(true, + &cost, &residuals, jacobians, scratch.get()), is_good);
diff --git a/internal/ceres/solver_impl.cc b/internal/ceres/solver_impl.cc index 16fdbf6..0ef0a27 100644 --- a/internal/ceres/solver_impl.cc +++ b/internal/ceres/solver_impl.cc
@@ -914,8 +914,11 @@ // The residual is constant and will be removed, so its cost is // added to the variable fixed_cost. double cost = 0.0; - if (!residual_block->Evaluate( - &cost, NULL, NULL, residual_block_evaluate_scratch.get())) { + if (!residual_block->Evaluate(true, + &cost, + NULL, + NULL, + residual_block_evaluate_scratch.get())) { *error = StringPrintf("Evaluation of the residual %d failed during " "removal of fixed residual blocks.", i); return false;
diff --git a/internal/ceres/solver_impl_test.cc b/internal/ceres/solver_impl_test.cc index 8e443ba..2471ea2 100644 --- a/internal/ceres/solver_impl_test.cc +++ b/internal/ceres/solver_impl_test.cc
@@ -242,7 +242,8 @@ ResidualBlock *expected_removed_block = program.residual_blocks()[0]; scoped_array<double> scratch( new double[expected_removed_block->NumScratchDoublesForEvaluate()]); - expected_removed_block->Evaluate(&expected_fixed_cost, + expected_removed_block->Evaluate(true, + &expected_fixed_cost, NULL, NULL, scratch.get());
diff --git a/internal/ceres/trust_region_minimizer_test.cc b/internal/ceres/trust_region_minimizer_test.cc index 4bc9409..9c2780b 100644 --- a/internal/ceres/trust_region_minimizer_test.cc +++ b/internal/ceres/trust_region_minimizer_test.cc
@@ -82,7 +82,8 @@ return dense_jacobian; } - virtual bool Evaluate(const double* state, + virtual bool Evaluate(const Evaluator::EvaluateOptions& evaluate_options, + const double* state, double* cost, double* residuals, double* /* gradient */,