• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Ceres Solver - A fast non-linear least squares minimizer
2 // Copyright 2010, 2011, 2012 Google Inc. All rights reserved.
3 // http://code.google.com/p/ceres-solver/
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are met:
7 //
8 // * Redistributions of source code must retain the above copyright notice,
9 //   this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above copyright notice,
11 //   this list of conditions and the following disclaimer in the documentation
12 //   and/or other materials provided with the distribution.
13 // * Neither the name of Google Inc. nor the names of its contributors may be
14 //   used to endorse or promote products derived from this software without
15 //   specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 // POSSIBILITY OF SUCH DAMAGE.
28 //
29 // Author: sameeragarwal@google.com (Sameer Agarwal)
30 
31 #include "ceres/loss_function.h"
32 
33 #include <cstddef>
34 
35 #include "glog/logging.h"
36 #include "gtest/gtest.h"
37 
38 namespace ceres {
39 namespace internal {
40 namespace {
41 
42 // Helper function for testing a LossFunction callback.
43 //
44 // Compares the values of rho'(s) and rho''(s) computed by the
45 // callback with estimates obtained by symmetric finite differencing
46 // of rho(s).
AssertLossFunctionIsValid(const LossFunction & loss,double s)47 void AssertLossFunctionIsValid(const LossFunction& loss, double s) {
48   CHECK_GT(s, 0);
49 
50   // Evaluate rho(s), rho'(s) and rho''(s).
51   double rho[3];
52   loss.Evaluate(s, rho);
53 
54   // Use symmetric finite differencing to estimate rho'(s) and
55   // rho''(s).
56   const double kH = 1e-4;
57   // Values at s + kH.
58   double fwd[3];
59   // Values at s - kH.
60   double bwd[3];
61   loss.Evaluate(s + kH, fwd);
62   loss.Evaluate(s - kH, bwd);
63 
64   // First derivative.
65   const double fd_1 = (fwd[0] - bwd[0]) / (2 * kH);
66   ASSERT_NEAR(fd_1, rho[1], 1e-6);
67 
68   // Second derivative.
69   const double fd_2 = (fwd[0] - 2*rho[0] + bwd[0]) / (kH * kH);
70   ASSERT_NEAR(fd_2, rho[2], 1e-6);
71 }
72 }  // namespace
73 
74 // Try two values of the scaling a = 0.7 and 1.3
75 // (where scaling makes sense) and of the squared norm
76 // s = 0.357 and 1.792
77 //
78 // Note that for the Huber loss the test exercises both code paths
79 //  (i.e. both small and large values of s).
80 
TEST(LossFunction,TrivialLoss)81 TEST(LossFunction, TrivialLoss) {
82   AssertLossFunctionIsValid(TrivialLoss(), 0.357);
83   AssertLossFunctionIsValid(TrivialLoss(), 1.792);
84 }
85 
TEST(LossFunction,HuberLoss)86 TEST(LossFunction, HuberLoss) {
87   AssertLossFunctionIsValid(HuberLoss(0.7), 0.357);
88   AssertLossFunctionIsValid(HuberLoss(0.7), 1.792);
89   AssertLossFunctionIsValid(HuberLoss(1.3), 0.357);
90   AssertLossFunctionIsValid(HuberLoss(1.3), 1.792);
91 }
92 
TEST(LossFunction,SoftLOneLoss)93 TEST(LossFunction, SoftLOneLoss) {
94   AssertLossFunctionIsValid(SoftLOneLoss(0.7), 0.357);
95   AssertLossFunctionIsValid(SoftLOneLoss(0.7), 1.792);
96   AssertLossFunctionIsValid(SoftLOneLoss(1.3), 0.357);
97   AssertLossFunctionIsValid(SoftLOneLoss(1.3), 1.792);
98 }
99 
TEST(LossFunction,CauchyLoss)100 TEST(LossFunction, CauchyLoss) {
101   AssertLossFunctionIsValid(CauchyLoss(0.7), 0.357);
102   AssertLossFunctionIsValid(CauchyLoss(0.7), 1.792);
103   AssertLossFunctionIsValid(CauchyLoss(1.3), 0.357);
104   AssertLossFunctionIsValid(CauchyLoss(1.3), 1.792);
105 }
106 
TEST(LossFunction,ArctanLoss)107 TEST(LossFunction, ArctanLoss) {
108   AssertLossFunctionIsValid(ArctanLoss(0.7), 0.357);
109   AssertLossFunctionIsValid(ArctanLoss(0.7), 1.792);
110   AssertLossFunctionIsValid(ArctanLoss(1.3), 0.357);
111   AssertLossFunctionIsValid(ArctanLoss(1.3), 1.792);
112 }
113 
TEST(LossFunction,TolerantLoss)114 TEST(LossFunction, TolerantLoss) {
115   AssertLossFunctionIsValid(TolerantLoss(0.7, 0.4), 0.357);
116   AssertLossFunctionIsValid(TolerantLoss(0.7, 0.4), 1.792);
117   AssertLossFunctionIsValid(TolerantLoss(0.7, 0.4), 55.5);
118   AssertLossFunctionIsValid(TolerantLoss(1.3, 0.1), 0.357);
119   AssertLossFunctionIsValid(TolerantLoss(1.3, 0.1), 1.792);
120   AssertLossFunctionIsValid(TolerantLoss(1.3, 0.1), 55.5);
121   // Check the value at zero is actually zero.
122   double rho[3];
123   TolerantLoss(0.7, 0.4).Evaluate(0.0, rho);
124   ASSERT_NEAR(rho[0], 0.0, 1e-6);
125   // Check that loss before and after the approximation threshold are good.
126   // A threshold of 36.7 is used by the implementation.
127   AssertLossFunctionIsValid(TolerantLoss(20.0, 1.0), 20.0 + 36.6);
128   AssertLossFunctionIsValid(TolerantLoss(20.0, 1.0), 20.0 + 36.7);
129   AssertLossFunctionIsValid(TolerantLoss(20.0, 1.0), 20.0 + 36.8);
130   AssertLossFunctionIsValid(TolerantLoss(20.0, 1.0), 20.0 + 1000.0);
131 }
132 
TEST(LossFunction,ComposedLoss)133 TEST(LossFunction, ComposedLoss) {
134   {
135     HuberLoss f(0.7);
136     CauchyLoss g(1.3);
137     ComposedLoss c(&f, DO_NOT_TAKE_OWNERSHIP, &g, DO_NOT_TAKE_OWNERSHIP);
138     AssertLossFunctionIsValid(c, 0.357);
139     AssertLossFunctionIsValid(c, 1.792);
140   }
141   {
142     CauchyLoss f(0.7);
143     HuberLoss g(1.3);
144     ComposedLoss c(&f, DO_NOT_TAKE_OWNERSHIP, &g, DO_NOT_TAKE_OWNERSHIP);
145     AssertLossFunctionIsValid(c, 0.357);
146     AssertLossFunctionIsValid(c, 1.792);
147   }
148 }
149 
TEST(LossFunction,ScaledLoss)150 TEST(LossFunction, ScaledLoss) {
151   // Wrap a few loss functions, and a few scale factors. This can't combine
152   // construction with the call to AssertLossFunctionIsValid() because Apple's
153   // GCC is unable to eliminate the copy of ScaledLoss, which is not copyable.
154   {
155     ScaledLoss scaled_loss(NULL, 6, TAKE_OWNERSHIP);
156     AssertLossFunctionIsValid(scaled_loss, 0.323);
157   }
158   {
159     ScaledLoss scaled_loss(new TrivialLoss(), 10, TAKE_OWNERSHIP);
160     AssertLossFunctionIsValid(scaled_loss, 0.357);
161   }
162   {
163     ScaledLoss scaled_loss(new HuberLoss(0.7), 0.1, TAKE_OWNERSHIP);
164     AssertLossFunctionIsValid(scaled_loss, 1.792);
165   }
166   {
167     ScaledLoss scaled_loss(new SoftLOneLoss(1.3), 0.1, TAKE_OWNERSHIP);
168     AssertLossFunctionIsValid(scaled_loss, 1.792);
169   }
170   {
171     ScaledLoss scaled_loss(new CauchyLoss(1.3), 10, TAKE_OWNERSHIP);
172     AssertLossFunctionIsValid(scaled_loss, 1.792);
173   }
174   {
175     ScaledLoss scaled_loss(new ArctanLoss(1.3), 10, TAKE_OWNERSHIP);
176     AssertLossFunctionIsValid(scaled_loss, 1.792);
177   }
178   {
179     ScaledLoss scaled_loss(
180         new TolerantLoss(1.3, 0.1), 10, TAKE_OWNERSHIP);
181     AssertLossFunctionIsValid(scaled_loss, 1.792);
182   }
183   {
184     ScaledLoss scaled_loss(
185         new ComposedLoss(
186             new HuberLoss(0.8), TAKE_OWNERSHIP,
187             new TolerantLoss(1.3, 0.5), TAKE_OWNERSHIP), 10, TAKE_OWNERSHIP);
188     AssertLossFunctionIsValid(scaled_loss, 1.792);
189   }
190 }
191 
TEST(LossFunction,LossFunctionWrapper)192 TEST(LossFunction, LossFunctionWrapper) {
193   // Initialization
194   HuberLoss loss_function1(1.0);
195   LossFunctionWrapper loss_function_wrapper(new HuberLoss(1.0),
196                                             TAKE_OWNERSHIP);
197 
198   double s = 0.862;
199   double rho_gold[3];
200   double rho[3];
201   loss_function1.Evaluate(s, rho_gold);
202   loss_function_wrapper.Evaluate(s, rho);
203   for (int i = 0; i < 3; ++i) {
204     EXPECT_NEAR(rho[i], rho_gold[i], 1e-12);
205   }
206 
207   // Resetting
208   HuberLoss loss_function2(0.5);
209   loss_function_wrapper.Reset(new HuberLoss(0.5), TAKE_OWNERSHIP);
210   loss_function_wrapper.Evaluate(s, rho);
211   loss_function2.Evaluate(s, rho_gold);
212   for (int i = 0; i < 3; ++i) {
213     EXPECT_NEAR(rho[i], rho_gold[i], 1e-12);
214   }
215 
216   // Not taking ownership.
217   HuberLoss loss_function3(0.3);
218   loss_function_wrapper.Reset(&loss_function3, DO_NOT_TAKE_OWNERSHIP);
219   loss_function_wrapper.Evaluate(s, rho);
220   loss_function3.Evaluate(s, rho_gold);
221   for (int i = 0; i < 3; ++i) {
222     EXPECT_NEAR(rho[i], rho_gold[i], 1e-12);
223   }
224 }
225 
226 }  // namespace internal
227 }  // namespace ceres
228