Eigen-Contrib  5.0.1
 
Loading...
Searching...
No Matches
PolynomialSolver.h
1// This file is part of Eigen, a lightweight C++ template library
2// for linear algebra.
3//
4// Copyright (C) 2010 Manuel Yguel <manuel.yguel@gmail.com>
5//
6// This Source Code Form is subject to the terms of the Mozilla
7// Public License v. 2.0. If a copy of the MPL was not distributed
8// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
9// SPDX-License-Identifier: MPL-2.0
10
11#ifndef EIGEN_POLYNOMIAL_SOLVER_H
12#define EIGEN_POLYNOMIAL_SOLVER_H
13
14// IWYU pragma: private
15#include "./InternalHeaderCheck.h"
16
17namespace Eigen {
18
32template <typename Scalar_, int Deg_>
33class PolynomialSolverBase {
34 public:
35 EIGEN_MAKE_ALIGNED_OPERATOR_NEW_IF_VECTORIZABLE_FIXED_SIZE(Scalar_, Deg_ == Dynamic ? Dynamic : Deg_)
36
37 typedef Scalar_ Scalar;
38 typedef typename NumTraits<Scalar>::Real RealScalar;
39 typedef internal::make_complex_t<Scalar> RootType;
40 typedef Matrix<RootType, Deg_, 1> RootsType;
41
42 typedef DenseIndex Index;
43
44 protected:
45 template <typename OtherPolynomial>
46 inline void setPolynomial(const OtherPolynomial& poly) {
47 m_roots.resize(poly.size() - 1);
48 }
49
50 public:
51 template <typename OtherPolynomial>
52 inline PolynomialSolverBase(const OtherPolynomial& poly) {
53 setPolynomial(poly());
54 }
55
56 inline PolynomialSolverBase() {}
57
58 public:
60 inline const RootsType& roots() const { return m_roots; }
61
62 public:
73 template <typename Stl_back_insertion_sequence>
74 inline void realRoots(Stl_back_insertion_sequence& bi_seq,
75 const RealScalar& absImaginaryThreshold = NumTraits<Scalar>::dummy_precision()) const {
76 using std::abs;
77 bi_seq.clear();
78 for (Index i = 0; i < m_roots.size(); ++i) {
79 if (abs(m_roots[i].imag()) < absImaginaryThreshold) {
80 bi_seq.push_back(m_roots[i].real());
81 }
82 }
83 }
84
85 protected:
86 template <typename Predicate>
87 inline const RootType& selectComplexRoot_withRespectToNorm(Predicate& pred) const {
88 Index res = 0;
89 RealScalar norm2 = numext::abs2(m_roots[0]);
90 for (Index i = 1; i < m_roots.size(); ++i) {
91 const RealScalar currNorm2 = numext::abs2(m_roots[i]);
92 if (pred(currNorm2, norm2)) {
93 res = i;
94 norm2 = currNorm2;
95 }
96 }
97 return m_roots[res];
98 }
99
100 public:
104 inline const RootType& greatestRoot() const {
105 std::greater<RealScalar> greater;
106 return selectComplexRoot_withRespectToNorm(greater);
107 }
108
112 inline const RootType& smallestRoot() const {
113 std::less<RealScalar> less;
114 return selectComplexRoot_withRespectToNorm(less);
115 }
116
117 protected:
118 template <typename Predicate>
119 inline const RealScalar& selectRealRoot_withRespectToAbsRealPart(
120 Predicate& pred, bool& hasArealRoot,
121 const RealScalar& absImaginaryThreshold = NumTraits<Scalar>::dummy_precision()) const {
122 using std::abs;
123 hasArealRoot = false;
124 Index res = 0;
125 RealScalar val(0);
126
127 for (Index i = 0; i < m_roots.size(); ++i) {
128 if (abs(m_roots[i].imag()) <= absImaginaryThreshold) {
129 if (!hasArealRoot) {
130 hasArealRoot = true;
131 res = i;
132 val = abs(m_roots[i].real());
133 } else {
134 const RealScalar curr = abs(m_roots[i].real());
135 if (pred(curr, val)) {
136 val = curr;
137 res = i;
138 }
139 }
140 } else if (!hasArealRoot) {
141 if (abs(m_roots[i].imag()) < abs(m_roots[res].imag())) {
142 res = i;
143 }
144 }
145 }
146 return numext::real_ref(m_roots[res]);
147 }
148
149 template <typename Predicate>
150 inline const RealScalar& selectRealRoot_withRespectToRealPart(
151 Predicate& pred, bool& hasArealRoot,
152 const RealScalar& absImaginaryThreshold = NumTraits<Scalar>::dummy_precision()) const {
153 using std::abs;
154 hasArealRoot = false;
155 Index res = 0;
156 RealScalar val(0);
157
158 for (Index i = 0; i < m_roots.size(); ++i) {
159 if (abs(m_roots[i].imag()) <= absImaginaryThreshold) {
160 if (!hasArealRoot) {
161 hasArealRoot = true;
162 res = i;
163 val = m_roots[i].real();
164 } else {
165 const RealScalar curr = m_roots[i].real();
166 if (pred(curr, val)) {
167 val = curr;
168 res = i;
169 }
170 }
171 } else {
172 if (abs(m_roots[i].imag()) < abs(m_roots[res].imag())) {
173 res = i;
174 }
175 }
176 }
177 return numext::real_ref(m_roots[res]);
178 }
179
180 public:
195 inline const RealScalar& absGreatestRealRoot(
196 bool& hasArealRoot, const RealScalar& absImaginaryThreshold = NumTraits<Scalar>::dummy_precision()) const {
197 std::greater<RealScalar> greater;
198 return selectRealRoot_withRespectToAbsRealPart(greater, hasArealRoot, absImaginaryThreshold);
199 }
200
215 inline const RealScalar& absSmallestRealRoot(
216 bool& hasArealRoot, const RealScalar& absImaginaryThreshold = NumTraits<Scalar>::dummy_precision()) const {
217 std::less<RealScalar> less;
218 return selectRealRoot_withRespectToAbsRealPart(less, hasArealRoot, absImaginaryThreshold);
219 }
220
235 inline const RealScalar& greatestRealRoot(
236 bool& hasArealRoot, const RealScalar& absImaginaryThreshold = NumTraits<Scalar>::dummy_precision()) const {
237 std::greater<RealScalar> greater;
238 return selectRealRoot_withRespectToRealPart(greater, hasArealRoot, absImaginaryThreshold);
239 }
240
255 inline const RealScalar& smallestRealRoot(
256 bool& hasArealRoot, const RealScalar& absImaginaryThreshold = NumTraits<Scalar>::dummy_precision()) const {
257 std::less<RealScalar> less;
258 return selectRealRoot_withRespectToRealPart(less, hasArealRoot, absImaginaryThreshold);
259 }
260
261 protected:
262 RootsType m_roots;
263};
264
265#define EIGEN_POLYNOMIAL_SOLVER_BASE_INHERITED_TYPES(BASE) \
266 typedef typename BASE::Scalar Scalar; \
267 typedef typename BASE::RealScalar RealScalar; \
268 typedef typename BASE::RootType RootType; \
269 typedef typename BASE::RootsType RootsType;
270
299template <typename Scalar_, int Deg_>
300class PolynomialSolver : public PolynomialSolverBase<Scalar_, Deg_> {
301 public:
302 EIGEN_MAKE_ALIGNED_OPERATOR_NEW_IF_VECTORIZABLE_FIXED_SIZE(Scalar_, Deg_ == Dynamic ? Dynamic : Deg_)
303
304 typedef PolynomialSolverBase<Scalar_, Deg_> PS_Base;
305 EIGEN_POLYNOMIAL_SOLVER_BASE_INHERITED_TYPES(PS_Base)
306
307 typedef Matrix<Scalar, Deg_, Deg_> CompanionMatrixType;
308 typedef std::conditional_t<NumTraits<Scalar>::IsComplex, ComplexEigenSolver<CompanionMatrixType>,
310 EigenSolverType;
311 typedef internal::make_complex_t<Scalar_> ComplexScalar;
312
313 public:
315 template <typename OtherPolynomial>
316 void compute(const OtherPolynomial& poly) {
317 eigen_assert(Scalar(0) != poly[poly.size() - 1]);
318 eigen_assert(poly.size() > 1);
319 if (poly.size() > 2) {
320 internal::companion<Scalar, Deg_> companion(poly);
321 companion.balance();
322 m_eigenSolver.compute(companion.denseMatrix());
323 eigen_assert(m_eigenSolver.info() == Eigen::Success);
324 m_roots = m_eigenSolver.eigenvalues();
325 // Uniform coefficient scaling must not overflow or underflow the refinement's Horner recurrences.
326 const RealScalar scale = poly.realView().cwiseAbs().maxCoeff();
327 const auto scaledPoly = (poly / scale).eval();
328 refineRoots(scaledPoly);
329 cleanUpRoots(scaledPoly);
330 } else if (poly.size() == 2) {
331 m_roots.resize(1);
332 m_roots[0] = -poly[0] / poly[1];
333 }
334 }
335
336 public:
337 template <typename OtherPolynomial>
338 inline PolynomialSolver(const OtherPolynomial& poly) {
339 compute(poly);
340 }
341
342 inline PolynomialSolver() {}
343
344 protected:
345 // Evaluates p(z) and p'(z) by Horner's rule. With withBound, returns Higham's running bound on the rounding error
346 // of the value (Higham 2002, Algorithm 5.1), otherwise zero: the moduli it needs cost more than the recurrence.
347 // The real bound is u (2 mu - |p(z)|), u = eps / 2; a complex product rounds by at most 2 sqrt(2) u |z| |y| and a
348 // complex sum by u |y|, so the complex bound is sqrt(2) eps (2 mu - |p(z)|).
349 template <typename OtherPolynomial>
350 static RealScalar evaluate(const OtherPolynomial& poly, const RootType& z, RootType& value, RootType& derivative,
351 bool withBound) {
352 const Index degree = poly.size() - 1;
353 value = RootType(poly[degree]);
354 derivative = RootType(0);
355 if (!withBound) {
356 for (Index k = degree - 1; k >= 0; --k) {
357 derivative = derivative * z + value;
358 value = value * z + RootType(poly[k]);
359 }
360 return RealScalar(0);
361 }
362 const RealScalar absz = numext::abs(z);
363 RealScalar mu = numext::abs(value) / RealScalar(2);
364 for (Index k = degree - 1; k >= 0; --k) {
365 derivative = derivative * z + value;
366 value = value * z + RootType(poly[k]);
367 mu = mu * absz + numext::abs(value);
368 }
369 const RealScalar errorScale = numext::sqrt(RealScalar(2)) * NumTraits<RealScalar>::epsilon();
370 return (RealScalar(2) * errorScale) * mu - errorScale * numext::abs(value);
371 }
372
381 template <typename OtherPolynomial>
382 void refineRoots(const OtherPolynomial& poly) {
383 const Index n = m_roots.size();
384 const RealScalar eps = NumTraits<RealScalar>::epsilon();
385 // Convergence is not guaranteed for clustered roots; retain the eigenvalue estimates as a fallback.
386 const int maxSweeps = NumTraits<RealScalar>::digits();
387 EIGEN_IF_CONSTEXPR (!NumTraits<Scalar>::IsComplex) {
388 for (Index i = 0; i < n; ++i) {
389 // Real iterates cannot reach a nonreal root; perturb by +/- i eps |z|.
390 if (numext::imag(m_roots[i]) == RealScalar(0)) {
391 const RealScalar perturbation = eps * numext::abs(m_roots[i]);
392 m_roots[i] += RootType(0, i % 2 == 0 ? perturbation : -perturbation);
393 }
394 }
395 }
397 RootType value, derivative;
398 for (int sweep = 0; sweep < maxSweeps; ++sweep) {
399 bool moving = false;
400 for (Index i = 0; i < n; ++i) {
401 if (!active[i]) continue;
402 const RootType z = m_roots[i];
403 // The eigenvalues can pass the rounding test with a backward error of ~10 eps, while one step from them
404 // lands near eps, so the test applies from the second sweep on.
405 const bool testResidual = sweep > 0;
406 const RealScalar bound = evaluate(poly, z, value, derivative, testResidual);
407 if ((testResidual && !(numext::abs(value) > bound)) || !(numext::isfinite)(bound) ||
408 derivative == RootType(0)) {
409 active[i] = false;
410 continue;
411 }
412 const RootType newton = value / derivative;
413 if (!(numext::abs(newton) > eps * numext::abs(z))) {
414 active[i] = false;
415 continue;
416 }
417 RootType repulsion(0);
418 for (Index j = 0; j < n; ++j) {
419 const RootType gap = z - m_roots[j];
420 if (j != i && gap != RootType(0)) repulsion += RootType(1) / gap;
421 }
422 const RootType denominator = RootType(1) - newton * repulsion;
423 const RootType refined = z - (denominator == RootType(0) ? newton : RootType(newton / denominator));
424 if ((numext::isfinite)(numext::abs(refined))) {
425 m_roots[i] = refined;
426 moving = true;
427 } else {
428 active[i] = false;
429 }
430 }
431 if (!moving) return;
432 }
433 // Keep improvements to slowly converging multiple roots, but reject a larger residual at the sweep cap.
434 for (Index i = 0; i < n; ++i) {
435 if (!active[i]) continue;
436 RootType initialValue;
437 evaluate(poly, m_roots[i], value, derivative, false);
438 evaluate(poly, m_eigenSolver.eigenvalues()[i], initialValue, derivative, false);
439 if (!(numext::abs(value) <= numext::abs(initialValue))) m_roots[i] = m_eigenSolver.eigenvalues()[i];
440 }
441 }
442
447 template <typename OtherPolynomial>
448 void cleanUpRoots(const OtherPolynomial& poly) {
449 const Index n = m_roots.size();
450 constexpr bool realPolynomial = !NumTraits<Scalar>::IsComplex;
452 EIGEN_IF_CONSTEXPR (realPolynomial) {
453 for (Index i = 0; i < n; ++i) {
454 if (!(numext::imag(m_roots[i]) > RealScalar(0))) continue;
455 Index partner = n;
456 RealScalar distance = numext::imag(m_roots[i]);
457 for (Index j = 0; j < n; ++j) {
458 if (paired[j] || !(numext::imag(m_roots[j]) < RealScalar(0))) continue;
459 const RealScalar d = numext::abs(m_roots[j] - numext::conj(m_roots[i]));
460 if (d < distance) {
461 distance = d;
462 partner = j;
463 }
464 }
465 if (partner < n) {
466 paired[i] = paired[partner] = true;
467 const RootType mean = (m_roots[i] + numext::conj(m_roots[partner])) / RealScalar(2);
468 m_roots[i] = mean;
469 m_roots[partner] = numext::conj(mean);
470 }
471 }
472 }
473 RootType value, derivative;
474 for (Index i = 0; i < n; ++i) {
475 if (numext::imag(m_roots[i]) == RealScalar(0)) continue;
476 const RootType realPart(numext::real(m_roots[i]));
477 if (realPolynomial && !paired[i]) {
478 m_roots[i] = realPart;
479 continue;
480 }
481 // A small residual at realPart can belong to a different root.
482 if (!(numext::abs(numext::imag(m_roots[i])) <=
483 numext::sqrt(NumTraits<RealScalar>::epsilon()) * numext::abs(realPart)))
484 continue;
485 const RealScalar bound = evaluate(poly, realPart, value, derivative, true);
486 if ((numext::isfinite)(bound) && numext::abs(value) <= bound) m_roots[i] = realPart;
487 }
488 }
489
490 using PS_Base::m_roots;
491 EigenSolverType m_eigenSolver;
492};
493
494template <typename Scalar_>
495class PolynomialSolver<Scalar_, 1> : public PolynomialSolverBase<Scalar_, 1> {
496 public:
497 typedef PolynomialSolverBase<Scalar_, 1> PS_Base;
498 EIGEN_POLYNOMIAL_SOLVER_BASE_INHERITED_TYPES(PS_Base)
499
500 public:
502 template <typename OtherPolynomial>
503 void compute(const OtherPolynomial& poly) {
504 eigen_assert(poly.size() == 2);
505 eigen_assert(Scalar(0) != poly[1]);
506 m_roots[0] = -poly[0] / poly[1];
507 }
508
509 public:
510 template <typename OtherPolynomial>
511 inline PolynomialSolver(const OtherPolynomial& poly) {
512 compute(poly);
513 }
514
515 inline PolynomialSolver() {}
516
517 protected:
518 using PS_Base::m_roots;
519};
520
521} // end namespace Eigen
522
523#endif // EIGEN_POLYNOMIAL_SOLVER_H
constexpr void resize(Index rows, Index cols)
const RealScalar & smallestRealRoot(bool &hasArealRoot, const RealScalar &absImaginaryThreshold=NumTraits< Scalar >::dummy_precision()) const
Definition PolynomialSolver.h:255
const RealScalar & absGreatestRealRoot(bool &hasArealRoot, const RealScalar &absImaginaryThreshold=NumTraits< Scalar >::dummy_precision()) const
Definition PolynomialSolver.h:195
const RealScalar & absSmallestRealRoot(bool &hasArealRoot, const RealScalar &absImaginaryThreshold=NumTraits< Scalar >::dummy_precision()) const
Definition PolynomialSolver.h:215
void realRoots(Stl_back_insertion_sequence &bi_seq, const RealScalar &absImaginaryThreshold=NumTraits< Scalar >::dummy_precision()) const
Definition PolynomialSolver.h:74
const RootType & greatestRoot() const
Definition PolynomialSolver.h:104
const RealScalar & greatestRealRoot(bool &hasArealRoot, const RealScalar &absImaginaryThreshold=NumTraits< Scalar >::dummy_precision()) const
Definition PolynomialSolver.h:235
const RootType & smallestRoot() const
Definition PolynomialSolver.h:112
const RootsType & roots() const
Definition PolynomialSolver.h:60
A polynomial solver.
Definition PolynomialSolver.h:300
void refineRoots(const OtherPolynomial &poly)
Definition PolynomialSolver.h:382
void compute(const OtherPolynomial &poly)
Definition PolynomialSolver.h:316
void cleanUpRoots(const OtherPolynomial &poly)
Definition PolynomialSolver.h:448
Namespace containing all symbols from the Eigen library.