blob: 983f11e114a7de565323e40cd0bbe84da141eb85 [file] [log] [blame]
Keir Mierle8ebb0732012-04-30 23:09:08 -07001// Ceres Solver - A fast non-linear least squares minimizer
Keir Mierle7492b0d2015-03-17 22:30:16 -07002// Copyright 2015 Google Inc. All rights reserved.
3// http://ceres-solver.org/
Keir Mierle8ebb0732012-04-30 23:09:08 -07004//
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: keir@google.com (Keir Mierle)
Tal Ben-Nun4f049db2015-05-13 15:43:51 +030030// tbennun@gmail.com (Tal Ben-Nun)
Keir Mierle8ebb0732012-04-30 23:09:08 -070031
32#include "ceres/numeric_diff_cost_function.h"
33
34#include <algorithm>
35#include <cmath>
36#include <string>
37#include <vector>
Keir Mierle8ebb0732012-04-30 23:09:08 -070038#include "ceres/internal/macros.h"
39#include "ceres/internal/scoped_ptr.h"
Sameer Agarwal2dd90772017-01-15 15:46:56 -080040#include "ceres/array_utils.h"
Sameer Agarwal2f0d7242013-01-18 13:11:32 -080041#include "ceres/numeric_diff_test_utils.h"
Sameer Agarwal0beab862012-08-13 15:12:01 -070042#include "ceres/test_util.h"
Keir Mierle8ebb0732012-04-30 23:09:08 -070043#include "ceres/types.h"
Sameer Agarwal0beab862012-08-13 15:12:01 -070044#include "glog/logging.h"
45#include "gtest/gtest.h"
Keir Mierle8ebb0732012-04-30 23:09:08 -070046
47namespace ceres {
48namespace internal {
49
Sameer Agarwal2f0d7242013-01-18 13:11:32 -080050TEST(NumericDiffCostFunction, EasyCaseFunctorCentralDifferences) {
51 internal::scoped_ptr<CostFunction> cost_function;
52 cost_function.reset(
53 new NumericDiffCostFunction<EasyFunctor,
54 CENTRAL,
55 3, /* number of residuals */
56 5, /* size of x1 */
57 5 /* size of x2 */>(
58 new EasyFunctor));
59 EasyFunctor functor;
60 functor.ExpectCostFunctionEvaluationIsNearlyCorrect(*cost_function, CENTRAL);
61}
Keir Mierle8ebb0732012-04-30 23:09:08 -070062
Sameer Agarwal2f0d7242013-01-18 13:11:32 -080063TEST(NumericDiffCostFunction, EasyCaseFunctorForwardDifferences) {
64 internal::scoped_ptr<CostFunction> cost_function;
65 cost_function.reset(
66 new NumericDiffCostFunction<EasyFunctor,
67 FORWARD,
68 3, /* number of residuals */
69 5, /* size of x1 */
70 5 /* size of x2 */>(
71 new EasyFunctor));
72 EasyFunctor functor;
73 functor.ExpectCostFunctionEvaluationIsNearlyCorrect(*cost_function, FORWARD);
74}
Sameer Agarwal2fc0ed62013-01-15 11:34:10 -080075
Tal Ben-Nun4f049db2015-05-13 15:43:51 +030076TEST(NumericDiffCostFunction, EasyCaseFunctorRidders) {
77 internal::scoped_ptr<CostFunction> cost_function;
78 cost_function.reset(
79 new NumericDiffCostFunction<EasyFunctor,
80 RIDDERS,
81 3, /* number of residuals */
82 5, /* size of x1 */
83 5 /* size of x2 */>(
84 new EasyFunctor));
85 EasyFunctor functor;
86 functor.ExpectCostFunctionEvaluationIsNearlyCorrect(*cost_function, RIDDERS);
87}
88
Sameer Agarwal2f0d7242013-01-18 13:11:32 -080089TEST(NumericDiffCostFunction, EasyCaseCostFunctionCentralDifferences) {
90 internal::scoped_ptr<CostFunction> cost_function;
91 cost_function.reset(
Sameer Agarwal2fc0ed62013-01-15 11:34:10 -080092 new NumericDiffCostFunction<EasyCostFunction,
Keir Mierle8ebb0732012-04-30 23:09:08 -070093 CENTRAL,
94 3, /* number of residuals */
95 5, /* size of x1 */
96 5 /* size of x2 */>(
Sameer Agarwal2fc0ed62013-01-15 11:34:10 -080097 new EasyCostFunction, TAKE_OWNERSHIP));
Sameer Agarwal2f0d7242013-01-18 13:11:32 -080098 EasyFunctor functor;
99 functor.ExpectCostFunctionEvaluationIsNearlyCorrect(*cost_function, CENTRAL);
100}
Keir Mierle8ebb0732012-04-30 23:09:08 -0700101
Sameer Agarwal2f0d7242013-01-18 13:11:32 -0800102TEST(NumericDiffCostFunction, EasyCaseCostFunctionForwardDifferences) {
103 internal::scoped_ptr<CostFunction> cost_function;
104 cost_function.reset(
Sameer Agarwal2fc0ed62013-01-15 11:34:10 -0800105 new NumericDiffCostFunction<EasyCostFunction,
Keir Mierle8ebb0732012-04-30 23:09:08 -0700106 FORWARD,
107 3, /* number of residuals */
108 5, /* size of x1 */
109 5 /* size of x2 */>(
Sameer Agarwal2fc0ed62013-01-15 11:34:10 -0800110 new EasyCostFunction, TAKE_OWNERSHIP));
Sameer Agarwal2f0d7242013-01-18 13:11:32 -0800111 EasyFunctor functor;
112 functor.ExpectCostFunctionEvaluationIsNearlyCorrect(*cost_function, FORWARD);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700113}
114
Tal Ben-Nun4f049db2015-05-13 15:43:51 +0300115TEST(NumericDiffCostFunction, EasyCaseCostFunctionRidders) {
116 internal::scoped_ptr<CostFunction> cost_function;
117 cost_function.reset(
118 new NumericDiffCostFunction<EasyCostFunction,
119 RIDDERS,
120 3, /* number of residuals */
121 5, /* size of x1 */
122 5 /* size of x2 */>(
123 new EasyCostFunction, TAKE_OWNERSHIP));
124 EasyFunctor functor;
125 functor.ExpectCostFunctionEvaluationIsNearlyCorrect(*cost_function, RIDDERS);
126}
127
128TEST(NumericDiffCostFunction,
129 TranscendentalCaseFunctorCentralDifferences) {
Sameer Agarwal2f0d7242013-01-18 13:11:32 -0800130 internal::scoped_ptr<CostFunction> cost_function;
131 cost_function.reset(
Sameer Agarwal2fc0ed62013-01-15 11:34:10 -0800132 new NumericDiffCostFunction<TranscendentalFunctor,
133 CENTRAL,
134 2, /* number of residuals */
135 5, /* size of x1 */
136 5 /* size of x2 */>(
Sameer Agarwal2f0d7242013-01-18 13:11:32 -0800137 new TranscendentalFunctor));
138 TranscendentalFunctor functor;
139 functor.ExpectCostFunctionEvaluationIsNearlyCorrect(*cost_function, CENTRAL);
140}
Sameer Agarwal2fc0ed62013-01-15 11:34:10 -0800141
Tal Ben-Nun4f049db2015-05-13 15:43:51 +0300142TEST(NumericDiffCostFunction,
143 TranscendentalCaseFunctorForwardDifferences) {
Sameer Agarwal2f0d7242013-01-18 13:11:32 -0800144 internal::scoped_ptr<CostFunction> cost_function;
145 cost_function.reset(
Sameer Agarwal2fc0ed62013-01-15 11:34:10 -0800146 new NumericDiffCostFunction<TranscendentalFunctor,
147 FORWARD,
148 2, /* number of residuals */
149 5, /* size of x1 */
150 5 /* size of x2 */>(
Sameer Agarwal2f0d7242013-01-18 13:11:32 -0800151 new TranscendentalFunctor));
152 TranscendentalFunctor functor;
153 functor.ExpectCostFunctionEvaluationIsNearlyCorrect(*cost_function, FORWARD);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700154}
155
Tal Ben-Nun4f049db2015-05-13 15:43:51 +0300156TEST(NumericDiffCostFunction,
157 TranscendentalCaseFunctorRidders) {
158 NumericDiffOptions options;
159
160 // Using a smaller initial step size to overcome oscillatory function
161 // behavior.
162 options.ridders_relative_initial_step_size = 1e-3;
163
164 internal::scoped_ptr<CostFunction> cost_function;
165 cost_function.reset(
166 new NumericDiffCostFunction<TranscendentalFunctor,
167 RIDDERS,
168 2, /* number of residuals */
169 5, /* size of x1 */
170 5 /* size of x2 */>(
171 new TranscendentalFunctor, TAKE_OWNERSHIP, 2, options));
172 TranscendentalFunctor functor;
173 functor.ExpectCostFunctionEvaluationIsNearlyCorrect(*cost_function, RIDDERS);
174}
175
176TEST(NumericDiffCostFunction,
177 TranscendentalCaseCostFunctionCentralDifferences) {
Sameer Agarwal2f0d7242013-01-18 13:11:32 -0800178 internal::scoped_ptr<CostFunction> cost_function;
179 cost_function.reset(
180 new NumericDiffCostFunction<TranscendentalCostFunction,
181 CENTRAL,
182 2, /* number of residuals */
183 5, /* size of x1 */
184 5 /* size of x2 */>(
185 new TranscendentalCostFunction, TAKE_OWNERSHIP));
186 TranscendentalFunctor functor;
187 functor.ExpectCostFunctionEvaluationIsNearlyCorrect(*cost_function, CENTRAL);
188}
189
Tal Ben-Nun4f049db2015-05-13 15:43:51 +0300190TEST(NumericDiffCostFunction,
191 TranscendentalCaseCostFunctionForwardDifferences) {
Sameer Agarwal2f0d7242013-01-18 13:11:32 -0800192 internal::scoped_ptr<CostFunction> cost_function;
193 cost_function.reset(
194 new NumericDiffCostFunction<TranscendentalCostFunction,
195 FORWARD,
196 2, /* number of residuals */
197 5, /* size of x1 */
198 5 /* size of x2 */>(
199 new TranscendentalCostFunction, TAKE_OWNERSHIP));
200 TranscendentalFunctor functor;
201 functor.ExpectCostFunctionEvaluationIsNearlyCorrect(*cost_function, FORWARD);
202}
Sameer Agarwal295ade12012-08-22 06:51:22 -0700203
Tal Ben-Nun4f049db2015-05-13 15:43:51 +0300204TEST(NumericDiffCostFunction,
205 TranscendentalCaseCostFunctionRidders) {
206 NumericDiffOptions options;
207
208 // Using a smaller initial step size to overcome oscillatory function
209 // behavior.
210 options.ridders_relative_initial_step_size = 1e-3;
211
212 internal::scoped_ptr<CostFunction> cost_function;
213 cost_function.reset(
214 new NumericDiffCostFunction<TranscendentalCostFunction,
215 RIDDERS,
216 2, /* number of residuals */
217 5, /* size of x1 */
218 5 /* size of x2 */>(
219 new TranscendentalCostFunction, TAKE_OWNERSHIP, 2, options));
220 TranscendentalFunctor functor;
221 functor.ExpectCostFunctionEvaluationIsNearlyCorrect(*cost_function, RIDDERS);
222}
223
Sameer Agarwal295ade12012-08-22 06:51:22 -0700224template<int num_rows, int num_cols>
225class SizeTestingCostFunction : public SizedCostFunction<num_rows, num_cols> {
226 public:
227 virtual bool Evaluate(double const* const* parameters,
228 double* residuals,
229 double** jacobians) const {
230 return true;
231 }
232};
233
234// As described in
235// http://forum.kde.org/viewtopic.php?f=74&t=98536#p210774
236// Eigen3 has restrictions on the Row/Column major storage of vectors,
237// depending on their dimensions. This test ensures that the correct
238// templates are instantiated for various shapes of the Jacobian
239// matrix.
240TEST(NumericDiffCostFunction, EigenRowMajorColMajorTest) {
241 scoped_ptr<CostFunction> cost_function;
242 cost_function.reset(
243 new NumericDiffCostFunction<SizeTestingCostFunction<1,1>, CENTRAL, 1, 1>(
244 new SizeTestingCostFunction<1,1>, ceres::TAKE_OWNERSHIP));
245
246 cost_function.reset(
247 new NumericDiffCostFunction<SizeTestingCostFunction<2,1>, CENTRAL, 2, 1>(
248 new SizeTestingCostFunction<2,1>, ceres::TAKE_OWNERSHIP));
249
250 cost_function.reset(
251 new NumericDiffCostFunction<SizeTestingCostFunction<1,2>, CENTRAL, 1, 2>(
252 new SizeTestingCostFunction<1,2>, ceres::TAKE_OWNERSHIP));
253
254 cost_function.reset(
255 new NumericDiffCostFunction<SizeTestingCostFunction<2,2>, CENTRAL, 2, 2>(
256 new SizeTestingCostFunction<2,2>, ceres::TAKE_OWNERSHIP));
Sameer Agarwal12eb3892014-08-27 22:18:33 -0700257
258 cost_function.reset(
259 new NumericDiffCostFunction<EasyFunctor, CENTRAL, ceres::DYNAMIC, 1, 1>(
260 new EasyFunctor, TAKE_OWNERSHIP, 1));
261
262 cost_function.reset(
263 new NumericDiffCostFunction<EasyFunctor, CENTRAL, ceres::DYNAMIC, 1, 1>(
264 new EasyFunctor, TAKE_OWNERSHIP, 2));
265
266 cost_function.reset(
267 new NumericDiffCostFunction<EasyFunctor, CENTRAL, ceres::DYNAMIC, 1, 2>(
268 new EasyFunctor, TAKE_OWNERSHIP, 1));
269
270 cost_function.reset(
271 new NumericDiffCostFunction<EasyFunctor, CENTRAL, ceres::DYNAMIC, 1, 2>(
272 new EasyFunctor, TAKE_OWNERSHIP, 2));
273
274 cost_function.reset(
275 new NumericDiffCostFunction<EasyFunctor, CENTRAL, ceres::DYNAMIC, 2, 1>(
276 new EasyFunctor, TAKE_OWNERSHIP, 1));
277
278 cost_function.reset(
279 new NumericDiffCostFunction<EasyFunctor, CENTRAL, ceres::DYNAMIC, 2, 1>(
280 new EasyFunctor, TAKE_OWNERSHIP, 2));
Sameer Agarwal295ade12012-08-22 06:51:22 -0700281}
282
Tal Ben-Nun4f049db2015-05-13 15:43:51 +0300283TEST(NumericDiffCostFunction,
284 EasyCaseFunctorCentralDifferencesAndDynamicNumResiduals) {
Sameer Agarwal3a2158d2013-10-03 07:12:14 -0700285 internal::scoped_ptr<CostFunction> cost_function;
286 cost_function.reset(
287 new NumericDiffCostFunction<EasyFunctor,
288 CENTRAL,
289 ceres::DYNAMIC,
290 5, /* size of x1 */
291 5 /* size of x2 */>(
292 new EasyFunctor, TAKE_OWNERSHIP, 3));
293 EasyFunctor functor;
294 functor.ExpectCostFunctionEvaluationIsNearlyCorrect(*cost_function, CENTRAL);
295}
296
Tal Ben-Nun4f049db2015-05-13 15:43:51 +0300297TEST(NumericDiffCostFunction, ExponentialFunctorRidders) {
298 internal::scoped_ptr<CostFunction> cost_function;
299 cost_function.reset(
300 new NumericDiffCostFunction<ExponentialFunctor,
301 RIDDERS,
302 1, /* number of residuals */
303 1 /* size of x1 */>(
304 new ExponentialFunctor));
305 ExponentialFunctor functor;
306 functor.ExpectCostFunctionEvaluationIsNearlyCorrect(*cost_function);
307}
308
309TEST(NumericDiffCostFunction, ExponentialCostFunctionRidders) {
310 internal::scoped_ptr<CostFunction> cost_function;
311 cost_function.reset(
312 new NumericDiffCostFunction<ExponentialCostFunction,
313 RIDDERS,
314 1, /* number of residuals */
315 1 /* size of x1 */>(
316 new ExponentialCostFunction));
317 ExponentialFunctor functor;
318 functor.ExpectCostFunctionEvaluationIsNearlyCorrect(*cost_function);
319}
320
321TEST(NumericDiffCostFunction, RandomizedFunctorRidders) {
322 internal::scoped_ptr<CostFunction> cost_function;
323 NumericDiffOptions options;
324 // Larger initial step size is chosen to produce robust results in the
325 // presence of random noise.
326 options.ridders_relative_initial_step_size = 10.0;
327
328 cost_function.reset(
329 new NumericDiffCostFunction<RandomizedFunctor,
330 RIDDERS,
331 1, /* number of residuals */
332 1 /* size of x1 */>(
333 new RandomizedFunctor(kNoiseFactor, kRandomSeed), TAKE_OWNERSHIP,
334 1, options));
335 RandomizedFunctor functor (kNoiseFactor, kRandomSeed);
336 functor.ExpectCostFunctionEvaluationIsNearlyCorrect(*cost_function);
337}
338
339TEST(NumericDiffCostFunction, RandomizedCostFunctionRidders) {
340 internal::scoped_ptr<CostFunction> cost_function;
341 NumericDiffOptions options;
342 // Larger initial step size is chosen to produce robust results in the
343 // presence of random noise.
344 options.ridders_relative_initial_step_size = 10.0;
345
346 cost_function.reset(
347 new NumericDiffCostFunction<RandomizedCostFunction,
348 RIDDERS,
349 1, /* number of residuals */
350 1 /* size of x1 */>(
351 new RandomizedCostFunction(kNoiseFactor, kRandomSeed),
352 TAKE_OWNERSHIP, 1, options));
353 RandomizedFunctor functor (kNoiseFactor, kRandomSeed);
354 functor.ExpectCostFunctionEvaluationIsNearlyCorrect(*cost_function);
355}
356
Sameer Agarwal2dd90772017-01-15 15:46:56 -0800357struct OnlyFillsOneOutputFunctor {
358 bool operator()(const double* x, double* output) const {
359 output[0] = x[0];
360 return true;
361 }
362};
363
364TEST(NumericDiffCostFunction, PartiallyFilledResidualShouldFailEvaluation) {
365 double parameter = 1.0;
366 double jacobian[2];
367 double residuals[2];
368 double* parameters[] = {&parameter};
369 double* jacobians[] = {jacobian};
370
371 scoped_ptr<CostFunction> cost_function(
372 new NumericDiffCostFunction<OnlyFillsOneOutputFunctor, CENTRAL, 2, 1>(
373 new OnlyFillsOneOutputFunctor));
374 InvalidateArray(2, jacobian);
375 InvalidateArray(2, residuals);
376 EXPECT_TRUE(cost_function->Evaluate(parameters, residuals, jacobians));
377 EXPECT_FALSE(IsArrayValid(2, residuals));
378 InvalidateArray(2, residuals);
379 EXPECT_TRUE(cost_function->Evaluate(parameters, residuals, NULL));
380 // We are only testing residuals here, because the Jacobians are
381 // computed using finite differencing from the residuals, so unless
382 // we introduce a validation step after every evaluation of
383 // residuals inside NumericDiffCostFunction, there is no way of
384 // ensuring that the Jacobian array is invalid.
385 EXPECT_FALSE(IsArrayValid(2, residuals));
386}
Tal Ben-Nun4f049db2015-05-13 15:43:51 +0300387
Keir Mierle8ebb0732012-04-30 23:09:08 -0700388} // namespace internal
389} // namespace ceres