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 31 #include "ceres/polynomial_solver.h" 32 33 #include <cmath> 34 #include <cstddef> 35 #include "Eigen/Dense" 36 #include "ceres/internal/port.h" 37 #include "glog/logging.h" 38 39 namespace ceres { 40 namespace internal { 41 namespace { 42 43 // Balancing function as described by B. N. Parlett and C. Reinsch, 44 // "Balancing a Matrix for Calculation of Eigenvalues and Eigenvectors". 45 // In: Numerische Mathematik, Volume 13, Number 4 (1969), 293-304, 46 // Springer Berlin / Heidelberg. DOI: 10.1007/BF02165404 47 void BalanceCompanionMatrix(Matrix* companion_matrix_ptr) { 48 CHECK_NOTNULL(companion_matrix_ptr); 49 Matrix& companion_matrix = *companion_matrix_ptr; 50 Matrix companion_matrix_offdiagonal = companion_matrix; 51 companion_matrix_offdiagonal.diagonal().setZero(); 52 53 const int degree = companion_matrix.rows(); 54 55 // gamma <= 1 controls how much a change in the scaling has to 56 // lower the 1-norm of the companion matrix to be accepted. 57 // 58 // gamma = 1 seems to lead to cycles (numerical issues?), so 59 // we set it slightly lower. 60 const double gamma = 0.9; 61 62 // Greedily scale row/column pairs until there is no change. 63 bool scaling_has_changed; 64 do { 65 scaling_has_changed = false; 66 67 for (int i = 0; i < degree; ++i) { 68 const double row_norm = companion_matrix_offdiagonal.row(i).lpNorm<1>(); 69 const double col_norm = companion_matrix_offdiagonal.col(i).lpNorm<1>(); 70 71 // Decompose row_norm/col_norm into mantissa * 2^exponent, 72 // where 0.5 <= mantissa < 1. Discard mantissa (return value 73 // of frexp), as only the exponent is needed. 74 int exponent = 0; 75 std::frexp(row_norm / col_norm, &exponent); 76 exponent /= 2; 77 78 if (exponent != 0) { 79 const double scaled_col_norm = std::ldexp(col_norm, exponent); 80 const double scaled_row_norm = std::ldexp(row_norm, -exponent); 81 if (scaled_col_norm + scaled_row_norm < gamma * (col_norm + row_norm)) { 82 // Accept the new scaling. (Multiplication by powers of 2 should not 83 // introduce rounding errors (ignoring non-normalized numbers and 84 // over- or underflow)) 85 scaling_has_changed = true; 86 companion_matrix_offdiagonal.row(i) *= std::ldexp(1.0, -exponent); 87 companion_matrix_offdiagonal.col(i) *= std::ldexp(1.0, exponent); 88 } 89 } 90 } 91 } while (scaling_has_changed); 92 93 companion_matrix_offdiagonal.diagonal() = companion_matrix.diagonal(); 94 companion_matrix = companion_matrix_offdiagonal; 95 VLOG(3) << "Balanced companion matrix is\n" << companion_matrix; 96 } 97 98 void BuildCompanionMatrix(const Vector& polynomial, 99 Matrix* companion_matrix_ptr) { 100 CHECK_NOTNULL(companion_matrix_ptr); 101 Matrix& companion_matrix = *companion_matrix_ptr; 102 103 const int degree = polynomial.size() - 1; 104 105 companion_matrix.resize(degree, degree); 106 companion_matrix.setZero(); 107 companion_matrix.diagonal(-1).setOnes(); 108 companion_matrix.col(degree - 1) = -polynomial.reverse().head(degree); 109 } 110 111 // Remove leading terms with zero coefficients. 112 Vector RemoveLeadingZeros(const Vector& polynomial_in) { 113 int i = 0; 114 while (i < (polynomial_in.size() - 1) && polynomial_in(i) == 0.0) { 115 ++i; 116 } 117 return polynomial_in.tail(polynomial_in.size() - i); 118 } 119 } // namespace 120 121 bool FindPolynomialRoots(const Vector& polynomial_in, 122 Vector* real, 123 Vector* imaginary) { 124 if (polynomial_in.size() == 0) { 125 LOG(ERROR) << "Invalid polynomial of size 0 passed to FindPolynomialRoots"; 126 return false; 127 } 128 129 Vector polynomial = RemoveLeadingZeros(polynomial_in); 130 const int degree = polynomial.size() - 1; 131 132 // Is the polynomial constant? 133 if (degree == 0) { 134 LOG(WARNING) << "Trying to extract roots from a constant " 135 << "polynomial in FindPolynomialRoots"; 136 return true; 137 } 138 139 // Divide by leading term 140 const double leading_term = polynomial(0); 141 polynomial /= leading_term; 142 143 // Separately handle linear polynomials. 144 if (degree == 1) { 145 if (real != NULL) { 146 real->resize(1); 147 (*real)(0) = -polynomial(1); 148 } 149 if (imaginary != NULL) { 150 imaginary->resize(1); 151 imaginary->setZero(); 152 } 153 } 154 155 // The degree is now known to be at least 2. 156 // Build and balance the companion matrix to the polynomial. 157 Matrix companion_matrix(degree, degree); 158 BuildCompanionMatrix(polynomial, &companion_matrix); 159 BalanceCompanionMatrix(&companion_matrix); 160 161 // Find its (complex) eigenvalues. 162 Eigen::EigenSolver<Matrix> solver(companion_matrix, false); 163 if (solver.info() != Eigen::Success) { 164 LOG(ERROR) << "Failed to extract eigenvalues from companion matrix."; 165 return false; 166 } 167 168 // Output roots 169 if (real != NULL) { 170 *real = solver.eigenvalues().real(); 171 } else { 172 LOG(WARNING) << "NULL pointer passed as real argument to " 173 << "FindPolynomialRoots. Real parts of the roots will not " 174 << "be returned."; 175 } 176 if (imaginary != NULL) { 177 *imaginary = solver.eigenvalues().imag(); 178 } 179 return true; 180 } 181 182 } // namespace internal 183 } // namespace ceres 184