1 // 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 (at) arcor.de (Markus Moll) 30 // sameeragarwal (at) 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 41 namespace ceres { 42 namespace internal { 43 namespace { 44 45 // For IEEE-754 doubles, machine precision is about 2e-16. 46 const double kEpsilon = 1e-13; 47 const double kEpsilonLoose = 1e-9; 48 49 // Return the constant polynomial p(x) = 1.23. 50 Vector 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). 57 Vector 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). 67 Vector 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. 79 Vector 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. 89 template<int N> 90 void 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 118 TEST(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 129 TEST(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 140 TEST(Polynomial, LinearPolynomialWithPositiveRootWorks) { 141 const double roots[1] = { 42.42 }; 142 RunPolynomialTestRealRoots(roots, true, true, kEpsilon); 143 } 144 145 TEST(Polynomial, LinearPolynomialWithNegativeRootWorks) { 146 const double roots[1] = { -42.42 }; 147 RunPolynomialTestRealRoots(roots, true, true, kEpsilon); 148 } 149 150 TEST(Polynomial, QuadraticPolynomialWithPositiveRootsWorks) { 151 const double roots[2] = { 1.0, 42.42 }; 152 RunPolynomialTestRealRoots(roots, true, true, kEpsilon); 153 } 154 155 TEST(Polynomial, QuadraticPolynomialWithOneNegativeRootWorks) { 156 const double roots[2] = { -42.42, 1.0 }; 157 RunPolynomialTestRealRoots(roots, true, true, kEpsilon); 158 } 159 160 TEST(Polynomial, QuadraticPolynomialWithTwoNegativeRootsWorks) { 161 const double roots[2] = { -42.42, -1.0 }; 162 RunPolynomialTestRealRoots(roots, true, true, kEpsilon); 163 } 164 165 TEST(Polynomial, QuadraticPolynomialWithCloseRootsWorks) { 166 const double roots[2] = { 42.42, 42.43 }; 167 RunPolynomialTestRealRoots(roots, true, false, kEpsilonLoose); 168 } 169 170 TEST(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 188 TEST(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 193 TEST(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 198 TEST(Polynomial, QuarticPolynomialWithTwoZeroRootsWorks) { 199 const double roots[4] = { -42.42, 0.0, 0.0, 42.42 }; 200 RunPolynomialTestRealRoots(roots, true, true, kEpsilonLoose); 201 } 202 203 TEST(Polynomial, QuarticMonomialWorks) { 204 const double roots[4] = { 0.0, 0.0, 0.0, 0.0 }; 205 RunPolynomialTestRealRoots(roots, true, true, kEpsilon); 206 } 207 208 TEST(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 213 TEST(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 218 TEST(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 223 TEST(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(), 1); 229 EXPECT_EQ(derivative(0), 0); 230 } 231 232 TEST(Polynomial, DifferentiateQuadraticPolynomial) { 233 // p(x) = x^2 + 2x + 3; 234 Vector polynomial(3); 235 polynomial(0) = 1.0; 236 polynomial(1) = 2.0; 237 polynomial(2) = 3.0; 238 239 const Vector derivative = DifferentiatePolynomial(polynomial); 240 EXPECT_EQ(derivative.rows(), 2); 241 EXPECT_EQ(derivative(0), 2.0); 242 EXPECT_EQ(derivative(1), 2.0); 243 } 244 245 TEST(Polynomial, MinimizeConstantPolynomial) { 246 // p(x) = 1; 247 Vector polynomial(1); 248 polynomial(0) = 1.0; 249 250 double optimal_x = 0.0; 251 double optimal_value = 0.0; 252 double min_x = 0.0; 253 double max_x = 1.0; 254 MinimizePolynomial(polynomial, min_x, max_x, &optimal_x, &optimal_value); 255 256 EXPECT_EQ(optimal_value, 1.0); 257 EXPECT_LE(optimal_x, max_x); 258 EXPECT_GE(optimal_x, min_x); 259 } 260 261 TEST(Polynomial, MinimizeLinearPolynomial) { 262 // p(x) = x - 2 263 Vector polynomial(2); 264 265 polynomial(0) = 1.0; 266 polynomial(1) = 2.0; 267 268 double optimal_x = 0.0; 269 double optimal_value = 0.0; 270 double min_x = 0.0; 271 double max_x = 1.0; 272 MinimizePolynomial(polynomial, min_x, max_x, &optimal_x, &optimal_value); 273 274 EXPECT_EQ(optimal_x, 0.0); 275 EXPECT_EQ(optimal_value, 2.0); 276 } 277 278 279 TEST(Polynomial, MinimizeQuadraticPolynomial) { 280 // p(x) = x^2 - 3 x + 2 281 // min_x = 3/2 282 // min_value = -1/4; 283 Vector polynomial(3); 284 polynomial(0) = 1.0; 285 polynomial(1) = -3.0; 286 polynomial(2) = 2.0; 287 288 double optimal_x = 0.0; 289 double optimal_value = 0.0; 290 double min_x = -2.0; 291 double max_x = 2.0; 292 MinimizePolynomial(polynomial, min_x, max_x, &optimal_x, &optimal_value); 293 EXPECT_EQ(optimal_x, 3.0/2.0); 294 EXPECT_EQ(optimal_value, -1.0/4.0); 295 296 min_x = -2.0; 297 max_x = 1.0; 298 MinimizePolynomial(polynomial, min_x, max_x, &optimal_x, &optimal_value); 299 EXPECT_EQ(optimal_x, 1.0); 300 EXPECT_EQ(optimal_value, 0.0); 301 302 min_x = 2.0; 303 max_x = 3.0; 304 MinimizePolynomial(polynomial, min_x, max_x, &optimal_x, &optimal_value); 305 EXPECT_EQ(optimal_x, 2.0); 306 EXPECT_EQ(optimal_value, 0.0); 307 } 308 309 TEST(Polymomial, ConstantInterpolatingPolynomial) { 310 // p(x) = 1.0 311 Vector true_polynomial(1); 312 true_polynomial << 1.0; 313 314 vector<FunctionSample> samples; 315 FunctionSample sample; 316 sample.x = 1.0; 317 sample.value = 1.0; 318 sample.value_is_valid = true; 319 samples.push_back(sample); 320 321 const Vector polynomial = FindInterpolatingPolynomial(samples); 322 EXPECT_NEAR((true_polynomial - polynomial).norm(), 0.0, 1e-15); 323 } 324 325 TEST(Polynomial, LinearInterpolatingPolynomial) { 326 // p(x) = 2x - 1 327 Vector true_polynomial(2); 328 true_polynomial << 2.0, -1.0; 329 330 vector<FunctionSample> samples; 331 FunctionSample sample; 332 sample.x = 1.0; 333 sample.value = 1.0; 334 sample.value_is_valid = true; 335 sample.gradient = 2.0; 336 sample.gradient_is_valid = true; 337 samples.push_back(sample); 338 339 const Vector polynomial = FindInterpolatingPolynomial(samples); 340 EXPECT_NEAR((true_polynomial - polynomial).norm(), 0.0, 1e-15); 341 } 342 343 TEST(Polynomial, QuadraticInterpolatingPolynomial) { 344 // p(x) = 2x^2 + 3x + 2 345 Vector true_polynomial(3); 346 true_polynomial << 2.0, 3.0, 2.0; 347 348 vector<FunctionSample> samples; 349 { 350 FunctionSample sample; 351 sample.x = 1.0; 352 sample.value = 7.0; 353 sample.value_is_valid = true; 354 sample.gradient = 7.0; 355 sample.gradient_is_valid = true; 356 samples.push_back(sample); 357 } 358 359 { 360 FunctionSample sample; 361 sample.x = -3.0; 362 sample.value = 11.0; 363 sample.value_is_valid = true; 364 samples.push_back(sample); 365 } 366 367 Vector polynomial = FindInterpolatingPolynomial(samples); 368 EXPECT_NEAR((true_polynomial - polynomial).norm(), 0.0, 1e-15); 369 } 370 371 TEST(Polynomial, DeficientCubicInterpolatingPolynomial) { 372 // p(x) = 2x^2 + 3x + 2 373 Vector true_polynomial(4); 374 true_polynomial << 0.0, 2.0, 3.0, 2.0; 375 376 vector<FunctionSample> samples; 377 { 378 FunctionSample sample; 379 sample.x = 1.0; 380 sample.value = 7.0; 381 sample.value_is_valid = true; 382 sample.gradient = 7.0; 383 sample.gradient_is_valid = true; 384 samples.push_back(sample); 385 } 386 387 { 388 FunctionSample sample; 389 sample.x = -3.0; 390 sample.value = 11.0; 391 sample.value_is_valid = true; 392 sample.gradient = -9; 393 sample.gradient_is_valid = true; 394 samples.push_back(sample); 395 } 396 397 const Vector polynomial = FindInterpolatingPolynomial(samples); 398 EXPECT_NEAR((true_polynomial - polynomial).norm(), 0.0, 1e-14); 399 } 400 401 402 TEST(Polynomial, CubicInterpolatingPolynomialFromValues) { 403 // p(x) = x^3 + 2x^2 + 3x + 2 404 Vector true_polynomial(4); 405 true_polynomial << 1.0, 2.0, 3.0, 2.0; 406 407 vector<FunctionSample> samples; 408 { 409 FunctionSample sample; 410 sample.x = 1.0; 411 sample.value = EvaluatePolynomial(true_polynomial, sample.x); 412 sample.value_is_valid = true; 413 samples.push_back(sample); 414 } 415 416 { 417 FunctionSample sample; 418 sample.x = -3.0; 419 sample.value = EvaluatePolynomial(true_polynomial, sample.x); 420 sample.value_is_valid = true; 421 samples.push_back(sample); 422 } 423 424 { 425 FunctionSample sample; 426 sample.x = 2.0; 427 sample.value = EvaluatePolynomial(true_polynomial, sample.x); 428 sample.value_is_valid = true; 429 samples.push_back(sample); 430 } 431 432 { 433 FunctionSample sample; 434 sample.x = 0.0; 435 sample.value = EvaluatePolynomial(true_polynomial, sample.x); 436 sample.value_is_valid = true; 437 samples.push_back(sample); 438 } 439 440 const Vector polynomial = FindInterpolatingPolynomial(samples); 441 EXPECT_NEAR((true_polynomial - polynomial).norm(), 0.0, 1e-14); 442 } 443 444 TEST(Polynomial, CubicInterpolatingPolynomialFromValuesAndOneGradient) { 445 // p(x) = x^3 + 2x^2 + 3x + 2 446 Vector true_polynomial(4); 447 true_polynomial << 1.0, 2.0, 3.0, 2.0; 448 Vector true_gradient_polynomial = DifferentiatePolynomial(true_polynomial); 449 450 vector<FunctionSample> samples; 451 { 452 FunctionSample sample; 453 sample.x = 1.0; 454 sample.value = EvaluatePolynomial(true_polynomial, sample.x); 455 sample.value_is_valid = true; 456 samples.push_back(sample); 457 } 458 459 { 460 FunctionSample sample; 461 sample.x = -3.0; 462 sample.value = EvaluatePolynomial(true_polynomial, sample.x); 463 sample.value_is_valid = true; 464 samples.push_back(sample); 465 } 466 467 { 468 FunctionSample sample; 469 sample.x = 2.0; 470 sample.value = EvaluatePolynomial(true_polynomial, sample.x); 471 sample.value_is_valid = true; 472 sample.gradient = EvaluatePolynomial(true_gradient_polynomial, sample.x); 473 sample.gradient_is_valid = true; 474 samples.push_back(sample); 475 } 476 477 const Vector polynomial = FindInterpolatingPolynomial(samples); 478 EXPECT_NEAR((true_polynomial - polynomial).norm(), 0.0, 1e-14); 479 } 480 481 TEST(Polynomial, CubicInterpolatingPolynomialFromValuesAndGradients) { 482 // p(x) = x^3 + 2x^2 + 3x + 2 483 Vector true_polynomial(4); 484 true_polynomial << 1.0, 2.0, 3.0, 2.0; 485 Vector true_gradient_polynomial = DifferentiatePolynomial(true_polynomial); 486 487 vector<FunctionSample> samples; 488 { 489 FunctionSample sample; 490 sample.x = -3.0; 491 sample.value = EvaluatePolynomial(true_polynomial, sample.x); 492 sample.value_is_valid = true; 493 sample.gradient = EvaluatePolynomial(true_gradient_polynomial, sample.x); 494 sample.gradient_is_valid = true; 495 samples.push_back(sample); 496 } 497 498 { 499 FunctionSample sample; 500 sample.x = 2.0; 501 sample.value = EvaluatePolynomial(true_polynomial, sample.x); 502 sample.value_is_valid = true; 503 sample.gradient = EvaluatePolynomial(true_gradient_polynomial, sample.x); 504 sample.gradient_is_valid = true; 505 samples.push_back(sample); 506 } 507 508 const Vector polynomial = FindInterpolatingPolynomial(samples); 509 EXPECT_NEAR((true_polynomial - polynomial).norm(), 0.0, 1e-14); 510 } 511 512 } // namespace internal 513 } // namespace ceres 514