ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
polynomial.cc
Go to the documentation of this file.
1 // Copyright (c) 2018, ETH Zurich and UNC Chapel Hill.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are met:
6 //
7 // * Redistributions of source code must retain the above copyright
8 // notice, this list of conditions and the following disclaimer.
9 //
10 // * Redistributions in binary form must reproduce the above copyright
11 // notice, this list of conditions and the following disclaimer in the
12 // documentation and/or other materials provided with the distribution.
13 //
14 // * Neither the name of ETH Zurich and UNC Chapel Hill nor the names of
15 // its contributors may be used to endorse or promote products derived
16 // from this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
22 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 // POSSIBILITY OF SUCH DAMAGE.
29 //
30 // Author: Johannes L. Schoenberger (jsch-at-demuc-dot-de)
31 
32 #include "base/polynomial.h"
33 
34 #include <Eigen/Eigenvalues>
35 
36 #include "util/logging.h"
37 
38 namespace colmap {
39 namespace {
40 
41 // Remove leading zero coefficients.
42 Eigen::VectorXd RemoveLeadingZeros(const Eigen::VectorXd& coeffs) {
43  Eigen::VectorXd::Index num_zeros = 0;
44  for (; num_zeros < coeffs.size(); ++num_zeros) {
45  if (coeffs(num_zeros) != 0) {
46  break;
47  }
48  }
49  return coeffs.tail(coeffs.size() - num_zeros);
50 }
51 
52 // Remove trailing zero coefficients.
53 Eigen::VectorXd RemoveTrailingZeros(const Eigen::VectorXd& coeffs) {
54  Eigen::VectorXd::Index num_zeros = 0;
55  for (; num_zeros < coeffs.size(); ++num_zeros) {
56  if (coeffs(coeffs.size() - 1 - num_zeros) != 0) {
57  break;
58  }
59  }
60  return coeffs.head(coeffs.size() - num_zeros);
61 }
62 
63 } // namespace
64 
65 bool FindLinearPolynomialRoots(const Eigen::VectorXd& coeffs,
66  Eigen::VectorXd* real, Eigen::VectorXd* imag) {
67  CHECK_EQ(coeffs.size(), 2);
68 
69  if (coeffs(0) == 0) {
70  return false;
71  }
72 
73  if (real != nullptr) {
74  real->resize(1);
75  (*real)(0) = -coeffs(1) / coeffs(0);
76  }
77 
78  if (imag != nullptr) {
79  imag->resize(1);
80  (*imag)(0) = 0;
81  }
82 
83  return true;
84 }
85 
86 bool FindQuadraticPolynomialRoots(const Eigen::VectorXd& coeffs,
87  Eigen::VectorXd* real,
88  Eigen::VectorXd* imag) {
89  CHECK_EQ(coeffs.size(), 3);
90 
91  const double a = coeffs(0);
92  if (a == 0) {
93  return FindLinearPolynomialRoots(coeffs.tail(2), real, imag);
94  }
95 
96  const double b = coeffs(1);
97  const double c = coeffs(2);
98  if (b == 0 && c == 0) {
99  if (real != nullptr) {
100  real->resize(1);
101  (*real)(0) = 0;
102  }
103  if (imag != nullptr) {
104  imag->resize(1);
105  (*imag)(0) = 0;
106  }
107  return true;
108  }
109 
110  const double d = b * b - 4 * a * c;
111 
112  if (d >= 0) {
113  const double sqrt_d = std::sqrt(d);
114  if (real != nullptr) {
115  real->resize(2);
116  if (b >= 0) {
117  (*real)(0) = (-b - sqrt_d) / (2 * a);
118  (*real)(1) = (2 * c) / (-b - sqrt_d);
119  } else {
120  (*real)(0) = (2 * c) / (-b + sqrt_d);
121  (*real)(1) = (-b + sqrt_d) / (2 * a);
122  }
123  }
124  if (imag != nullptr) {
125  imag->resize(2);
126  imag->setZero();
127  }
128  } else {
129  if (real != nullptr) {
130  real->resize(2);
131  real->setConstant(-b / (2 * a));
132  }
133  if (imag != nullptr) {
134  imag->resize(2);
135  (*imag)(0) = std::sqrt(-d) / (2 * a);
136  (*imag)(1) = -(*imag)(0);
137  }
138  }
139 
140  return true;
141 }
142 
143 bool FindPolynomialRootsDurandKerner(const Eigen::VectorXd& coeffs_all,
144  Eigen::VectorXd* real,
145  Eigen::VectorXd* imag) {
146  CHECK_GE(coeffs_all.size(), 2);
147 
148  const Eigen::VectorXd coeffs = RemoveLeadingZeros(coeffs_all);
149 
150  const int degree = coeffs.size() - 1;
151 
152  if (degree <= 0) {
153  return false;
154  } else if (degree == 1) {
155  return FindLinearPolynomialRoots(coeffs, real, imag);
156  } else if (degree == 2) {
157  return FindQuadraticPolynomialRoots(coeffs, real, imag);
158  }
159 
160  // Initialize roots.
161  Eigen::VectorXcd roots(degree);
162  roots(degree - 1) = std::complex<double>(1, 0);
163  for (int i = degree - 2; i >= 0; --i) {
164  roots(i) = roots(i + 1) * std::complex<double>(1, 1);
165  }
166 
167  // Iterative solver.
168  const int kMaxNumIterations = 100;
169  const double kMaxRootChange = 1e-10;
170  for (int iter = 0; iter < kMaxNumIterations; ++iter) {
171  double max_root_change = 0.0;
172  for (int i = 0; i < degree; ++i) {
173  const std::complex<double> root_i = roots(i);
174  std::complex<double> numerator = coeffs[0];
175  std::complex<double> denominator = coeffs[0];
176  for (int j = 0; j < degree; ++j) {
177  numerator = numerator * root_i + coeffs[j + 1];
178  if (i != j) {
179  denominator = denominator * (root_i - roots(j));
180  }
181  }
182  const std::complex<double> root_i_change = numerator / denominator;
183  roots(i) = root_i - root_i_change;
184  max_root_change =
185  std::max(max_root_change, std::abs(root_i_change.real()));
186  max_root_change =
187  std::max(max_root_change, std::abs(root_i_change.imag()));
188  }
189 
190  // Break, if roots do not change anymore.
191  if (max_root_change < kMaxRootChange) {
192  break;
193  }
194  }
195 
196  if (real != nullptr) {
197  real->resize(degree);
198  *real = roots.real();
199  }
200  if (imag != nullptr) {
201  imag->resize(degree);
202  *imag = roots.imag();
203  }
204 
205  return true;
206 }
207 
208 bool FindPolynomialRootsCompanionMatrix(const Eigen::VectorXd& coeffs_all,
209  Eigen::VectorXd* real,
210  Eigen::VectorXd* imag) {
211  CHECK_GE(coeffs_all.size(), 2);
212 
213  Eigen::VectorXd coeffs = RemoveLeadingZeros(coeffs_all);
214 
215  const int degree = coeffs.size() - 1;
216 
217  if (degree <= 0) {
218  return false;
219  } else if (degree == 1) {
220  return FindLinearPolynomialRoots(coeffs, real, imag);
221  } else if (degree == 2) {
222  return FindQuadraticPolynomialRoots(coeffs, real, imag);
223  }
224 
225  // Remove the coefficients where zero is a solution.
226  coeffs = RemoveTrailingZeros(coeffs);
227 
228  // Check if only zero is a solution.
229  if (coeffs.size() == 1) {
230  if (real != nullptr) {
231  real->resize(1);
232  (*real)(0) = 0;
233  }
234  if (imag != nullptr) {
235  imag->resize(1);
236  (*imag)(0) = 0;
237  }
238  return true;
239  }
240 
241  // Fill the companion matrix.
242  Eigen::MatrixXd C(coeffs.size() - 1, coeffs.size() - 1);
243  C.setZero();
244  for (Eigen::MatrixXd::Index i = 1; i < C.rows(); ++i) {
245  C(i, i - 1) = 1;
246  }
247  C.row(0) = -coeffs.tail(coeffs.size() - 1) / coeffs(0);
248 
249  // Solve for the roots of the polynomial.
250  Eigen::EigenSolver<Eigen::MatrixXd> solver(C, false);
251  if (solver.info() != Eigen::Success) {
252  return false;
253  }
254 
255  // If there are trailing zeros, we must add zero as a solution.
256  const int effective_degree =
257  coeffs.size() - 1 < degree ? coeffs.size() : coeffs.size() - 1;
258 
259  if (real != nullptr) {
260  real->resize(effective_degree);
261  real->head(coeffs.size() - 1) = solver.eigenvalues().real();
262  if (effective_degree > coeffs.size() - 1) {
263  (*real)(real->size() - 1) = 0;
264  }
265  }
266  if (imag != nullptr) {
267  imag->resize(effective_degree);
268  imag->head(coeffs.size() - 1) = solver.eigenvalues().imag();
269  if (effective_degree > coeffs.size() - 1) {
270  (*imag)(imag->size() - 1) = 0;
271  }
272  }
273 
274  return true;
275 }
276 
277 } // namespace colmap
coeffs(0)
a[190]
const double * e
bool FindPolynomialRootsCompanionMatrix(const Eigen::VectorXd &coeffs_all, Eigen::VectorXd *real, Eigen::VectorXd *imag)
Definition: polynomial.cc:208
bool FindLinearPolynomialRoots(const Eigen::VectorXd &coeffs, Eigen::VectorXd *real, Eigen::VectorXd *imag)
Definition: polynomial.cc:65
bool FindQuadraticPolynomialRoots(const Eigen::VectorXd &coeffs, Eigen::VectorXd *real, Eigen::VectorXd *imag)
Definition: polynomial.cc:86
bool FindPolynomialRootsDurandKerner(const Eigen::VectorXd &coeffs_all, Eigen::VectorXd *real, Eigen::VectorXd *imag)
Definition: polynomial.cc:143
Eigen::MatrixXd::Index Index
Definition: knncpp.h:26