| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288 | // Ceres Solver - A fast non-linear least squares minimizer// Copyright 2015 Google Inc. All rights reserved.// http://ceres-solver.org///// Redistribution and use in source and binary forms, with or without// modification, are permitted provided that the following conditions are met://// * Redistributions of source code must retain the above copyright notice,//   this list of conditions and the following disclaimer.// * Redistributions in binary form must reproduce the above copyright notice,//   this list of conditions and the following disclaimer in the documentation//   and/or other materials provided with the distribution.// * Neither the name of Google Inc. nor the names of its contributors may be//   used to endorse or promote products derived from this software without//   specific prior written permission.//// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE// POSSIBILITY OF SUCH DAMAGE.//// Author: sameeragarwal@google.com (Sameer Agarwal)#include "ceres/loss_function.h"#include <cstddef>#include "glog/logging.h"#include "gtest/gtest.h"namespace ceres {namespace internal {namespace {// Helper function for testing a LossFunction callback.//// Compares the values of rho'(s) and rho''(s) computed by the// callback with estimates obtained by symmetric finite differencing// of rho(s).void AssertLossFunctionIsValid(const LossFunction& loss, double s) {  CHECK_GT(s, 0);  // Evaluate rho(s), rho'(s) and rho''(s).  double rho[3];  loss.Evaluate(s, rho);  // Use symmetric finite differencing to estimate rho'(s) and  // rho''(s).  const double kH = 1e-4;  // Values at s + kH.  double fwd[3];  // Values at s - kH.  double bwd[3];  loss.Evaluate(s + kH, fwd);  loss.Evaluate(s - kH, bwd);  // First derivative.  const double fd_1 = (fwd[0] - bwd[0]) / (2 * kH);  ASSERT_NEAR(fd_1, rho[1], 1e-6);  // Second derivative.  const double fd_2 = (fwd[0] - 2*rho[0] + bwd[0]) / (kH * kH);  ASSERT_NEAR(fd_2, rho[2], 1e-6);}}  // namespace// Try two values of the scaling a = 0.7 and 1.3// (where scaling makes sense) and of the squared norm// s = 0.357 and 1.792//// Note that for the Huber loss the test exercises both code paths//  (i.e. both small and large values of s).TEST(LossFunction, TrivialLoss) {  AssertLossFunctionIsValid(TrivialLoss(), 0.357);  AssertLossFunctionIsValid(TrivialLoss(), 1.792);  // Check that at s = 0: rho = [0, 1, 0].  double rho[3];  TrivialLoss().Evaluate(0.0, rho);  ASSERT_NEAR(rho[0], 0.0, 1e-6);  ASSERT_NEAR(rho[1], 1.0, 1e-6);  ASSERT_NEAR(rho[2], 0.0, 1e-6);}TEST(LossFunction, HuberLoss) {  AssertLossFunctionIsValid(HuberLoss(0.7), 0.357);  AssertLossFunctionIsValid(HuberLoss(0.7), 1.792);  AssertLossFunctionIsValid(HuberLoss(1.3), 0.357);  AssertLossFunctionIsValid(HuberLoss(1.3), 1.792);  // Check that at s = 0: rho = [0, 1, 0].  double rho[3];  HuberLoss(0.7).Evaluate(0.0, rho);  ASSERT_NEAR(rho[0], 0.0, 1e-6);  ASSERT_NEAR(rho[1], 1.0, 1e-6);  ASSERT_NEAR(rho[2], 0.0, 1e-6);}TEST(LossFunction, SoftLOneLoss) {  AssertLossFunctionIsValid(SoftLOneLoss(0.7), 0.357);  AssertLossFunctionIsValid(SoftLOneLoss(0.7), 1.792);  AssertLossFunctionIsValid(SoftLOneLoss(1.3), 0.357);  AssertLossFunctionIsValid(SoftLOneLoss(1.3), 1.792);  // Check that at s = 0: rho = [0, 1, -1 / (2 * a^2)].  double rho[3];  SoftLOneLoss(0.7).Evaluate(0.0, rho);  ASSERT_NEAR(rho[0], 0.0, 1e-6);  ASSERT_NEAR(rho[1], 1.0, 1e-6);  ASSERT_NEAR(rho[2], -0.5 / (0.7 * 0.7), 1e-6);}TEST(LossFunction, CauchyLoss) {  AssertLossFunctionIsValid(CauchyLoss(0.7), 0.357);  AssertLossFunctionIsValid(CauchyLoss(0.7), 1.792);  AssertLossFunctionIsValid(CauchyLoss(1.3), 0.357);  AssertLossFunctionIsValid(CauchyLoss(1.3), 1.792);  // Check that at s = 0: rho = [0, 1, -1 / a^2].  double rho[3];  CauchyLoss(0.7).Evaluate(0.0, rho);  ASSERT_NEAR(rho[0], 0.0, 1e-6);  ASSERT_NEAR(rho[1], 1.0, 1e-6);  ASSERT_NEAR(rho[2], -1.0 / (0.7 * 0.7), 1e-6);}TEST(LossFunction, ArctanLoss) {  AssertLossFunctionIsValid(ArctanLoss(0.7), 0.357);  AssertLossFunctionIsValid(ArctanLoss(0.7), 1.792);  AssertLossFunctionIsValid(ArctanLoss(1.3), 0.357);  AssertLossFunctionIsValid(ArctanLoss(1.3), 1.792);  // Check that at s = 0: rho = [0, 1, 0].  double rho[3];  ArctanLoss(0.7).Evaluate(0.0, rho);  ASSERT_NEAR(rho[0], 0.0, 1e-6);  ASSERT_NEAR(rho[1], 1.0, 1e-6);  ASSERT_NEAR(rho[2], 0.0, 1e-6);}TEST(LossFunction, TolerantLoss) {  AssertLossFunctionIsValid(TolerantLoss(0.7, 0.4), 0.357);  AssertLossFunctionIsValid(TolerantLoss(0.7, 0.4), 1.792);  AssertLossFunctionIsValid(TolerantLoss(0.7, 0.4), 55.5);  AssertLossFunctionIsValid(TolerantLoss(1.3, 0.1), 0.357);  AssertLossFunctionIsValid(TolerantLoss(1.3, 0.1), 1.792);  AssertLossFunctionIsValid(TolerantLoss(1.3, 0.1), 55.5);  // Check the value at zero is actually zero.  double rho[3];  TolerantLoss(0.7, 0.4).Evaluate(0.0, rho);  ASSERT_NEAR(rho[0], 0.0, 1e-6);  // Check that loss before and after the approximation threshold are good.  // A threshold of 36.7 is used by the implementation.  AssertLossFunctionIsValid(TolerantLoss(20.0, 1.0), 20.0 + 36.6);  AssertLossFunctionIsValid(TolerantLoss(20.0, 1.0), 20.0 + 36.7);  AssertLossFunctionIsValid(TolerantLoss(20.0, 1.0), 20.0 + 36.8);  AssertLossFunctionIsValid(TolerantLoss(20.0, 1.0), 20.0 + 1000.0);}TEST(LossFunction, TukeyLoss) {  AssertLossFunctionIsValid(TukeyLoss(0.7), 0.357);  AssertLossFunctionIsValid(TukeyLoss(0.7), 1.792);  AssertLossFunctionIsValid(TukeyLoss(1.3), 0.357);  AssertLossFunctionIsValid(TukeyLoss(1.3), 1.792);  // Check that at s = 0: rho = [0, 1, -2 / a^2].  double rho[3];  TukeyLoss(0.7).Evaluate(0.0, rho);  ASSERT_NEAR(rho[0], 0.0, 1e-6);  ASSERT_NEAR(rho[1], 1.0, 1e-6);  ASSERT_NEAR(rho[2], -2.0 / (0.7 * 0.7), 1e-6);}TEST(LossFunction, ComposedLoss) {  {    HuberLoss f(0.7);    CauchyLoss g(1.3);    ComposedLoss c(&f, DO_NOT_TAKE_OWNERSHIP, &g, DO_NOT_TAKE_OWNERSHIP);    AssertLossFunctionIsValid(c, 0.357);    AssertLossFunctionIsValid(c, 1.792);  }  {    CauchyLoss f(0.7);    HuberLoss g(1.3);    ComposedLoss c(&f, DO_NOT_TAKE_OWNERSHIP, &g, DO_NOT_TAKE_OWNERSHIP);    AssertLossFunctionIsValid(c, 0.357);    AssertLossFunctionIsValid(c, 1.792);  }}TEST(LossFunction, ScaledLoss) {  // Wrap a few loss functions, and a few scale factors. This can't combine  // construction with the call to AssertLossFunctionIsValid() because Apple's  // GCC is unable to eliminate the copy of ScaledLoss, which is not copyable.  {    ScaledLoss scaled_loss(NULL, 6, TAKE_OWNERSHIP);    AssertLossFunctionIsValid(scaled_loss, 0.323);  }  {    ScaledLoss scaled_loss(new TrivialLoss(), 10, TAKE_OWNERSHIP);    AssertLossFunctionIsValid(scaled_loss, 0.357);  }  {    ScaledLoss scaled_loss(new HuberLoss(0.7), 0.1, TAKE_OWNERSHIP);    AssertLossFunctionIsValid(scaled_loss, 1.792);  }  {    ScaledLoss scaled_loss(new SoftLOneLoss(1.3), 0.1, TAKE_OWNERSHIP);    AssertLossFunctionIsValid(scaled_loss, 1.792);  }  {    ScaledLoss scaled_loss(new CauchyLoss(1.3), 10, TAKE_OWNERSHIP);    AssertLossFunctionIsValid(scaled_loss, 1.792);  }  {    ScaledLoss scaled_loss(new ArctanLoss(1.3), 10, TAKE_OWNERSHIP);    AssertLossFunctionIsValid(scaled_loss, 1.792);  }  {    ScaledLoss scaled_loss(        new TolerantLoss(1.3, 0.1), 10, TAKE_OWNERSHIP);    AssertLossFunctionIsValid(scaled_loss, 1.792);  }  {    ScaledLoss scaled_loss(        new ComposedLoss(            new HuberLoss(0.8), TAKE_OWNERSHIP,            new TolerantLoss(1.3, 0.5), TAKE_OWNERSHIP), 10, TAKE_OWNERSHIP);    AssertLossFunctionIsValid(scaled_loss, 1.792);  }}TEST(LossFunction, LossFunctionWrapper) {  // Initialization  HuberLoss loss_function1(1.0);  LossFunctionWrapper loss_function_wrapper(new HuberLoss(1.0),                                            TAKE_OWNERSHIP);  double s = 0.862;  double rho_gold[3];  double rho[3];  loss_function1.Evaluate(s, rho_gold);  loss_function_wrapper.Evaluate(s, rho);  for (int i = 0; i < 3; ++i) {    EXPECT_NEAR(rho[i], rho_gold[i], 1e-12);  }  // Resetting  HuberLoss loss_function2(0.5);  loss_function_wrapper.Reset(new HuberLoss(0.5), TAKE_OWNERSHIP);  loss_function_wrapper.Evaluate(s, rho);  loss_function2.Evaluate(s, rho_gold);  for (int i = 0; i < 3; ++i) {    EXPECT_NEAR(rho[i], rho_gold[i], 1e-12);  }  // Not taking ownership.  HuberLoss loss_function3(0.3);  loss_function_wrapper.Reset(&loss_function3, DO_NOT_TAKE_OWNERSHIP);  loss_function_wrapper.Evaluate(s, rho);  loss_function3.Evaluate(s, rho_gold);  for (int i = 0; i < 3; ++i) {    EXPECT_NEAR(rho[i], rho_gold[i], 1e-12);  }  // Set to NULL  TrivialLoss loss_function4;  loss_function_wrapper.Reset(NULL, TAKE_OWNERSHIP);  loss_function_wrapper.Evaluate(s, rho);  loss_function4.Evaluate(s, rho_gold);  for (int i = 0; i < 3; ++i) {    EXPECT_NEAR(rho[i], rho_gold[i], 1e-12);  }  // Set to NULL, not taking ownership  loss_function_wrapper.Reset(NULL, DO_NOT_TAKE_OWNERSHIP);  loss_function_wrapper.Evaluate(s, rho);  loss_function4.Evaluate(s, rho_gold);  for (int i = 0; i < 3; ++i) {    EXPECT_NEAR(rho[i], rho_gold[i], 1e-12);  }}}  // namespace internal}  // namespace ceres
 |