blob: f56352f623780e4763fd6cd78019d72a3d8de8fe [file] [log] [blame]
Sameer Agarwale7295c22012-11-23 18:56:50 -08001// Ceres Solver - A fast non-linear least squares minimizer
2// Copyright 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: moll.markus@arcor.de (Markus Moll)
30// sameeragarwal@google.com (Sameer Agarwal)
31
32#include "ceres/polynomial.h"
33
34#include <limits>
35#include <cmath>
36#include <cstddef>
37#include <algorithm>
38#include "gtest/gtest.h"
39#include "ceres/test_util.h"
40
41namespace ceres {
42namespace internal {
43namespace {
44
45// For IEEE-754 doubles, machine precision is about 2e-16.
46const double kEpsilon = 1e-13;
47const double kEpsilonLoose = 1e-9;
48
49// Return the constant polynomial p(x) = 1.23.
50Vector ConstantPolynomial(double value) {
51 Vector poly(1);
52 poly(0) = value;
53 return poly;
54}
55
56// Return the polynomial p(x) = poly(x) * (x - root).
57Vector AddRealRoot(const Vector& poly, double root) {
58 Vector poly2(poly.size() + 1);
59 poly2.setZero();
60 poly2.head(poly.size()) += poly;
61 poly2.tail(poly.size()) -= root * poly;
62 return poly2;
63}
64
65// Return the polynomial
66// p(x) = poly(x) * (x - real - imag*i) * (x - real + imag*i).
67Vector AddComplexRootPair(const Vector& poly, double real, double imag) {
68 Vector poly2(poly.size() + 2);
69 poly2.setZero();
70 // Multiply poly by x^2 - 2real + abs(real,imag)^2
71 poly2.head(poly.size()) += poly;
72 poly2.segment(1, poly.size()) -= 2 * real * poly;
73 poly2.tail(poly.size()) += (real*real + imag*imag) * poly;
74 return poly2;
75}
76
77// Sort the entries in a vector.
78// Needed because the roots are not returned in sorted order.
79Vector SortVector(const Vector& in) {
80 Vector out(in);
81 std::sort(out.data(), out.data() + out.size());
82 return out;
83}
84
85// Run a test with the polynomial defined by the N real roots in roots_real.
86// If use_real is false, NULL is passed as the real argument to
87// FindPolynomialRoots. If use_imaginary is false, NULL is passed as the
88// imaginary argument to FindPolynomialRoots.
89template<int N>
90void RunPolynomialTestRealRoots(const double (&real_roots)[N],
91 bool use_real,
92 bool use_imaginary,
93 double epsilon) {
94 Vector real;
95 Vector imaginary;
96 Vector poly = ConstantPolynomial(1.23);
97 for (int i = 0; i < N; ++i) {
98 poly = AddRealRoot(poly, real_roots[i]);
99 }
100 Vector* const real_ptr = use_real ? &real : NULL;
101 Vector* const imaginary_ptr = use_imaginary ? &imaginary : NULL;
102 bool success = FindPolynomialRoots(poly, real_ptr, imaginary_ptr);
103
104 EXPECT_EQ(success, true);
105 if (use_real) {
106 EXPECT_EQ(real.size(), N);
107 real = SortVector(real);
108 ExpectArraysClose(N, real.data(), real_roots, epsilon);
109 }
110 if (use_imaginary) {
111 EXPECT_EQ(imaginary.size(), N);
112 const Vector zeros = Vector::Zero(N);
113 ExpectArraysClose(N, imaginary.data(), zeros.data(), epsilon);
114 }
115}
116} // namespace
117
118TEST(Polynomial, InvalidPolynomialOfZeroLengthIsRejected) {
119 // Vector poly(0) is an ambiguous constructor call, so
120 // use the constructor with explicit column count.
121 Vector poly(0, 1);
122 Vector real;
123 Vector imag;
124 bool success = FindPolynomialRoots(poly, &real, &imag);
125
126 EXPECT_EQ(success, false);
127}
128
129TEST(Polynomial, ConstantPolynomialReturnsNoRoots) {
130 Vector poly = ConstantPolynomial(1.23);
131 Vector real;
132 Vector imag;
133 bool success = FindPolynomialRoots(poly, &real, &imag);
134
135 EXPECT_EQ(success, true);
136 EXPECT_EQ(real.size(), 0);
137 EXPECT_EQ(imag.size(), 0);
138}
139
140TEST(Polynomial, LinearPolynomialWithPositiveRootWorks) {
141 const double roots[1] = { 42.42 };
142 RunPolynomialTestRealRoots(roots, true, true, kEpsilon);
143}
144
145TEST(Polynomial, LinearPolynomialWithNegativeRootWorks) {
146 const double roots[1] = { -42.42 };
147 RunPolynomialTestRealRoots(roots, true, true, kEpsilon);
148}
149
150TEST(Polynomial, QuadraticPolynomialWithPositiveRootsWorks) {
151 const double roots[2] = { 1.0, 42.42 };
152 RunPolynomialTestRealRoots(roots, true, true, kEpsilon);
153}
154
155TEST(Polynomial, QuadraticPolynomialWithOneNegativeRootWorks) {
156 const double roots[2] = { -42.42, 1.0 };
157 RunPolynomialTestRealRoots(roots, true, true, kEpsilon);
158}
159
160TEST(Polynomial, QuadraticPolynomialWithTwoNegativeRootsWorks) {
161 const double roots[2] = { -42.42, -1.0 };
162 RunPolynomialTestRealRoots(roots, true, true, kEpsilon);
163}
164
165TEST(Polynomial, QuadraticPolynomialWithCloseRootsWorks) {
166 const double roots[2] = { 42.42, 42.43 };
167 RunPolynomialTestRealRoots(roots, true, false, kEpsilonLoose);
168}
169
170TEST(Polynomial, QuadraticPolynomialWithComplexRootsWorks) {
171 Vector real;
172 Vector imag;
173
174 Vector poly = ConstantPolynomial(1.23);
175 poly = AddComplexRootPair(poly, 42.42, 4.2);
176 bool success = FindPolynomialRoots(poly, &real, &imag);
177
178 EXPECT_EQ(success, true);
179 EXPECT_EQ(real.size(), 2);
180 EXPECT_EQ(imag.size(), 2);
181 ExpectClose(real(0), 42.42, kEpsilon);
182 ExpectClose(real(1), 42.42, kEpsilon);
183 ExpectClose(std::abs(imag(0)), 4.2, kEpsilon);
184 ExpectClose(std::abs(imag(1)), 4.2, kEpsilon);
185 ExpectClose(std::abs(imag(0) + imag(1)), 0.0, kEpsilon);
186}
187
188TEST(Polynomial, QuarticPolynomialWorks) {
189 const double roots[4] = { 1.23e-4, 1.23e-1, 1.23e+2, 1.23e+5 };
190 RunPolynomialTestRealRoots(roots, true, true, kEpsilon);
191}
192
193TEST(Polynomial, QuarticPolynomialWithTwoClustersOfCloseRootsWorks) {
194 const double roots[4] = { 1.23e-1, 2.46e-1, 1.23e+5, 2.46e+5 };
195 RunPolynomialTestRealRoots(roots, true, true, kEpsilonLoose);
196}
197
198TEST(Polynomial, QuarticPolynomialWithTwoZeroRootsWorks) {
199 const double roots[4] = { -42.42, 0.0, 0.0, 42.42 };
200 RunPolynomialTestRealRoots(roots, true, true, kEpsilonLoose);
201}
202
203TEST(Polynomial, QuarticMonomialWorks) {
204 const double roots[4] = { 0.0, 0.0, 0.0, 0.0 };
205 RunPolynomialTestRealRoots(roots, true, true, kEpsilon);
206}
207
208TEST(Polynomial, NullPointerAsImaginaryPartWorks) {
209 const double roots[4] = { 1.23e-4, 1.23e-1, 1.23e+2, 1.23e+5 };
210 RunPolynomialTestRealRoots(roots, true, false, kEpsilon);
211}
212
213TEST(Polynomial, NullPointerAsRealPartWorks) {
214 const double roots[4] = { 1.23e-4, 1.23e-1, 1.23e+2, 1.23e+5 };
215 RunPolynomialTestRealRoots(roots, false, true, kEpsilon);
216}
217
218TEST(Polynomial, BothOutputArgumentsNullWorks) {
219 const double roots[4] = { 1.23e-4, 1.23e-1, 1.23e+2, 1.23e+5 };
220 RunPolynomialTestRealRoots(roots, false, false, kEpsilon);
221}
222
223TEST(Polynomial, DifferentiateConstantPolynomial) {
224 // p(x) = 1;
225 Vector polynomial(1);
226 polynomial(0) = 1.0;
227 const Vector derivative = DifferentiatePolynomial(polynomial);
228 EXPECT_EQ(derivative.rows(), 0);
229}
230
231TEST(Polynomial, DifferentiateQuadraticPolynomial) {
232 // p(x) = x^2 + 2x + 3;
233 Vector polynomial(3);
234 polynomial(0) = 1.0;
235 polynomial(1) = 2.0;
236 polynomial(2) = 3.0;
237
238 const Vector derivative = DifferentiatePolynomial(polynomial);
239 EXPECT_EQ(derivative.rows(), 2);
240 EXPECT_EQ(derivative(0), 2.0);
241 EXPECT_EQ(derivative(1), 2.0);
242}
243
244TEST(Polynomial, MinimizeConstantPolynomial) {
245 // p(x) = 1;
246 Vector polynomial(1);
247 polynomial(0) = 1.0;
248
249 double optimal_x = 0.0;
250 double optimal_value = 0.0;
251 double min_x = 0.0;
252 double max_x = 1.0;
253 MinimizePolynomial(polynomial, min_x, max_x, &optimal_x, &optimal_value);
254
255 EXPECT_EQ(optimal_value, 1.0);
256 EXPECT_LE(optimal_x, max_x);
257 EXPECT_GE(optimal_x, min_x);
258}
259
260TEST(Polynomial, MinimizeLinearPolynomial) {
261 // p(x) = x - 2
262 Vector polynomial(2);
263
264 polynomial(0) = 1.0;
265 polynomial(1) = 2.0;
266
267 double optimal_x = 0.0;
268 double optimal_value = 0.0;
269 double min_x = 0.0;
270 double max_x = 1.0;
271 MinimizePolynomial(polynomial, min_x, max_x, &optimal_x, &optimal_value);
272
273 EXPECT_EQ(optimal_x, 0.0);
274 EXPECT_EQ(optimal_value, 2.0);
275}
276
277
278TEST(Polynomial, MinimizeQuadraticPolynomial) {
279 // p(x) = x^2 - 3 x + 2
280 // min_x = 3/2
281 // min_value = -1/4;
282 Vector polynomial(3);
283 polynomial(0) = 1.0;
284 polynomial(1) = -3.0;
285 polynomial(2) = 2.0;
286
287 double optimal_x = 0.0;
288 double optimal_value = 0.0;
289 double min_x = -2.0;
290 double max_x = 2.0;
291 MinimizePolynomial(polynomial, min_x, max_x, &optimal_x, &optimal_value);
292 EXPECT_EQ(optimal_x, 3.0/2.0);
293 EXPECT_EQ(optimal_value, -1.0/4.0);
294
295 min_x = -2.0;
296 max_x = 1.0;
297 MinimizePolynomial(polynomial, min_x, max_x, &optimal_x, &optimal_value);
298 EXPECT_EQ(optimal_x, 1.0);
299 EXPECT_EQ(optimal_value, 0.0);
300
301 min_x = 2.0;
302 max_x = 3.0;
303 MinimizePolynomial(polynomial, min_x, max_x, &optimal_x, &optimal_value);
304 EXPECT_EQ(optimal_x, 2.0);
305 EXPECT_EQ(optimal_value, 0.0);
306}
307
308TEST(Polymomial, ConstantInterpolatingPolynomial) {
309 // p(x) = 1.0
310 Vector true_polynomial(1);
311 true_polynomial << 1.0;
312
313 vector<FunctionSample> samples;
314 FunctionSample sample;
315 sample.x = 1.0;
316 sample.value = 1.0;
317 sample.value_is_valid = true;
318 samples.push_back(sample);
319
320 const Vector polynomial = FindInterpolatingPolynomial(samples);
321 EXPECT_NEAR((true_polynomial - polynomial).norm(), 0.0, 1e-15);
322}
323
324TEST(Polynomial, LinearInterpolatingPolynomial) {
325 // p(x) = 2x - 1
326 Vector true_polynomial(2);
327 true_polynomial << 2.0, -1.0;
328
329 vector<FunctionSample> samples;
330 FunctionSample sample;
331 sample.x = 1.0;
332 sample.value = 1.0;
333 sample.value_is_valid = true;
334 sample.gradient = 2.0;
335 sample.gradient_is_valid = true;
336 samples.push_back(sample);
337
338 const Vector polynomial = FindInterpolatingPolynomial(samples);
339 EXPECT_NEAR((true_polynomial - polynomial).norm(), 0.0, 1e-15);
340}
341
342TEST(Polynomial, QuadraticInterpolatingPolynomial) {
343 // p(x) = 2x^2 + 3x + 2
344 Vector true_polynomial(3);
345 true_polynomial << 2.0, 3.0, 2.0;
346
347 vector<FunctionSample> samples;
348 {
349 FunctionSample sample;
350 sample.x = 1.0;
351 sample.value = 7.0;
352 sample.value_is_valid = true;
353 sample.gradient = 7.0;
354 sample.gradient_is_valid = true;
355 samples.push_back(sample);
356 }
357
358 {
359 FunctionSample sample;
360 sample.x = -3.0;
361 sample.value = 11.0;
362 sample.value_is_valid = true;
363 samples.push_back(sample);
364 }
365
366 Vector polynomial = FindInterpolatingPolynomial(samples);
367 EXPECT_NEAR((true_polynomial - polynomial).norm(), 0.0, 1e-15);
368}
369
370TEST(Polynomial, DeficientCubicInterpolatingPolynomial) {
371 // p(x) = 2x^2 + 3x + 2
372 Vector true_polynomial(4);
373 true_polynomial << 0.0, 2.0, 3.0, 2.0;
374
375 vector<FunctionSample> samples;
376 {
377 FunctionSample sample;
378 sample.x = 1.0;
379 sample.value = 7.0;
380 sample.value_is_valid = true;
381 sample.gradient = 7.0;
382 sample.gradient_is_valid = true;
383 samples.push_back(sample);
384 }
385
386 {
387 FunctionSample sample;
388 sample.x = -3.0;
389 sample.value = 11.0;
390 sample.value_is_valid = true;
391 sample.gradient = -9;
392 sample.gradient_is_valid = true;
393 samples.push_back(sample);
394 }
395
396 const Vector polynomial = FindInterpolatingPolynomial(samples);
397 EXPECT_NEAR((true_polynomial - polynomial).norm(), 0.0, 1e-14);
398}
399
400
401TEST(Polynomial, CubicInterpolatingPolynomialFromValues) {
402 // p(x) = x^3 + 2x^2 + 3x + 2
403 Vector true_polynomial(4);
404 true_polynomial << 1.0, 2.0, 3.0, 2.0;
405
406 vector<FunctionSample> samples;
407 {
408 FunctionSample sample;
409 sample.x = 1.0;
410 sample.value = EvaluatePolynomial(true_polynomial, sample.x);
411 sample.value_is_valid = true;
412 samples.push_back(sample);
413 }
414
415 {
416 FunctionSample sample;
417 sample.x = -3.0;
418 sample.value = EvaluatePolynomial(true_polynomial, sample.x);
419 sample.value_is_valid = true;
420 samples.push_back(sample);
421 }
422
423 {
424 FunctionSample sample;
425 sample.x = 2.0;
426 sample.value = EvaluatePolynomial(true_polynomial, sample.x);
427 sample.value_is_valid = true;
428 samples.push_back(sample);
429 }
430
431 {
432 FunctionSample sample;
433 sample.x = 0.0;
434 sample.value = EvaluatePolynomial(true_polynomial, sample.x);
435 sample.value_is_valid = true;
436 samples.push_back(sample);
437 }
438
439 const Vector polynomial = FindInterpolatingPolynomial(samples);
440 EXPECT_NEAR((true_polynomial - polynomial).norm(), 0.0, 1e-14);
441}
442
443TEST(Polynomial, CubicInterpolatingPolynomialFromValuesAndOneGradient) {
444 // p(x) = x^3 + 2x^2 + 3x + 2
445 Vector true_polynomial(4);
446 true_polynomial << 1.0, 2.0, 3.0, 2.0;
447 Vector true_gradient_polynomial = DifferentiatePolynomial(true_polynomial);
448
449 vector<FunctionSample> samples;
450 {
451 FunctionSample sample;
452 sample.x = 1.0;
453 sample.value = EvaluatePolynomial(true_polynomial, sample.x);
454 sample.value_is_valid = true;
455 samples.push_back(sample);
456 }
457
458 {
459 FunctionSample sample;
460 sample.x = -3.0;
461 sample.value = EvaluatePolynomial(true_polynomial, sample.x);
462 sample.value_is_valid = true;
463 samples.push_back(sample);
464 }
465
466 {
467 FunctionSample sample;
468 sample.x = 2.0;
469 sample.value = EvaluatePolynomial(true_polynomial, sample.x);
470 sample.value_is_valid = true;
471 sample.gradient = EvaluatePolynomial(true_gradient_polynomial, sample.x);
472 sample.gradient_is_valid = true;
473 samples.push_back(sample);
474 }
475
476 const Vector polynomial = FindInterpolatingPolynomial(samples);
477 EXPECT_NEAR((true_polynomial - polynomial).norm(), 0.0, 1e-14);
478}
479
480TEST(Polynomial, CubicInterpolatingPolynomialFromValuesAndGradients) {
481 // p(x) = x^3 + 2x^2 + 3x + 2
482 Vector true_polynomial(4);
483 true_polynomial << 1.0, 2.0, 3.0, 2.0;
484 Vector true_gradient_polynomial = DifferentiatePolynomial(true_polynomial);
485
486 vector<FunctionSample> samples;
487 {
488 FunctionSample sample;
489 sample.x = -3.0;
490 sample.value = EvaluatePolynomial(true_polynomial, sample.x);
491 sample.value_is_valid = true;
492 sample.gradient = EvaluatePolynomial(true_gradient_polynomial, sample.x);
493 sample.gradient_is_valid = true;
494 samples.push_back(sample);
495 }
496
497 {
498 FunctionSample sample;
499 sample.x = 2.0;
500 sample.value = EvaluatePolynomial(true_polynomial, sample.x);
501 sample.value_is_valid = true;
502 sample.gradient = EvaluatePolynomial(true_gradient_polynomial, sample.x);
503 sample.gradient_is_valid = true;
504 samples.push_back(sample);
505 }
506
507 const Vector polynomial = FindInterpolatingPolynomial(samples);
508 EXPECT_NEAR((true_polynomial - polynomial).norm(), 0.0, 1e-14);
509}
510
511} // namespace internal
512} // namespace ceres