blob: 8c0ce3b71d2a7daa78d466cea63a3be64712745d [file] [log] [blame]
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2010 Manuel Yguel <manuel.yguel@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef EIGEN_POLYNOMIAL_SOLVER_H
#define EIGEN_POLYNOMIAL_SOLVER_H
// IWYU pragma: private
#include "./InternalHeaderCheck.h"
namespace Eigen {
/** \ingroup Polynomials_Module
* \class PolynomialSolverBase.
*
* \brief Defined to be inherited by polynomial solvers: it provides
* convenient methods such as
* - real roots,
* - greatest, smallest complex roots,
* - real roots with greatest, smallest absolute real value,
* - greatest, smallest real roots.
*
* It stores the set of roots as a vector of complexes.
*
*/
template <typename Scalar_, int Deg_>
class PolynomialSolverBase {
public:
EIGEN_MAKE_ALIGNED_OPERATOR_NEW_IF_VECTORIZABLE_FIXED_SIZE(Scalar_, Deg_ == Dynamic ? Dynamic : Deg_)
typedef Scalar_ Scalar;
typedef typename NumTraits<Scalar>::Real RealScalar;
typedef std::complex<RealScalar> RootType;
typedef Matrix<RootType, Deg_, 1> RootsType;
typedef DenseIndex Index;
protected:
template <typename OtherPolynomial>
inline void setPolynomial(const OtherPolynomial& poly) {
m_roots.resize(poly.size() - 1);
}
public:
template <typename OtherPolynomial>
inline PolynomialSolverBase(const OtherPolynomial& poly) {
setPolynomial(poly());
}
inline PolynomialSolverBase() {}
public:
/** \returns the complex roots of the polynomial */
inline const RootsType& roots() const { return m_roots; }
public:
/** Clear and fills the back insertion sequence with the real roots of the polynomial
* i.e. the real part of the complex roots that have an imaginary part which
* absolute value is smaller than absImaginaryThreshold.
* absImaginaryThreshold takes the dummy_precision associated
* with the Scalar_ template parameter of the PolynomialSolver class as the default value.
*
* \param[out] bi_seq : the back insertion sequence (stl concept)
* \param[in] absImaginaryThreshold : the maximum bound of the imaginary part of a complex
* number that is considered as real.
* */
template <typename Stl_back_insertion_sequence>
inline void realRoots(Stl_back_insertion_sequence& bi_seq,
const RealScalar& absImaginaryThreshold = NumTraits<Scalar>::dummy_precision()) const {
using std::abs;
bi_seq.clear();
for (Index i = 0; i < m_roots.size(); ++i) {
if (abs(m_roots[i].imag()) < absImaginaryThreshold) {
bi_seq.push_back(m_roots[i].real());
}
}
}
protected:
template <typename squaredNormBinaryPredicate>
inline const RootType& selectComplexRoot_withRespectToNorm(squaredNormBinaryPredicate& pred) const {
Index res = 0;
RealScalar norm2 = numext::abs2(m_roots[0]);
for (Index i = 1; i < m_roots.size(); ++i) {
const RealScalar currNorm2 = numext::abs2(m_roots[i]);
if (pred(currNorm2, norm2)) {
res = i;
norm2 = currNorm2;
}
}
return m_roots[res];
}
public:
/**
* \returns the complex root with greatest norm.
*/
inline const RootType& greatestRoot() const {
std::greater<RealScalar> greater;
return selectComplexRoot_withRespectToNorm(greater);
}
/**
* \returns the complex root with smallest norm.
*/
inline const RootType& smallestRoot() const {
std::less<RealScalar> less;
return selectComplexRoot_withRespectToNorm(less);
}
protected:
template <typename squaredRealPartBinaryPredicate>
inline const RealScalar& selectRealRoot_withRespectToAbsRealPart(
squaredRealPartBinaryPredicate& pred, bool& hasArealRoot,
const RealScalar& absImaginaryThreshold = NumTraits<Scalar>::dummy_precision()) const {
using std::abs;
hasArealRoot = false;
Index res = 0;
RealScalar abs2(0);
for (Index i = 0; i < m_roots.size(); ++i) {
if (abs(m_roots[i].imag()) <= absImaginaryThreshold) {
if (!hasArealRoot) {
hasArealRoot = true;
res = i;
abs2 = m_roots[i].real() * m_roots[i].real();
} else {
const RealScalar currAbs2 = m_roots[i].real() * m_roots[i].real();
if (pred(currAbs2, abs2)) {
abs2 = currAbs2;
res = i;
}
}
} else if (!hasArealRoot) {
if (abs(m_roots[i].imag()) < abs(m_roots[res].imag())) {
res = i;
}
}
}
return numext::real_ref(m_roots[res]);
}
template <typename RealPartBinaryPredicate>
inline const RealScalar& selectRealRoot_withRespectToRealPart(
RealPartBinaryPredicate& pred, bool& hasArealRoot,
const RealScalar& absImaginaryThreshold = NumTraits<Scalar>::dummy_precision()) const {
using std::abs;
hasArealRoot = false;
Index res = 0;
RealScalar val(0);
for (Index i = 0; i < m_roots.size(); ++i) {
if (abs(m_roots[i].imag()) <= absImaginaryThreshold) {
if (!hasArealRoot) {
hasArealRoot = true;
res = i;
val = m_roots[i].real();
} else {
const RealScalar curr = m_roots[i].real();
if (pred(curr, val)) {
val = curr;
res = i;
}
}
} else {
if (abs(m_roots[i].imag()) < abs(m_roots[res].imag())) {
res = i;
}
}
}
return numext::real_ref(m_roots[res]);
}
public:
/**
* \returns a real root with greatest absolute magnitude.
* A real root is defined as the real part of a complex root with absolute imaginary
* part smallest than absImaginaryThreshold.
* absImaginaryThreshold takes the dummy_precision associated
* with the Scalar_ template parameter of the PolynomialSolver class as the default value.
* If no real root is found the boolean hasArealRoot is set to false and the real part of
* the root with smallest absolute imaginary part is returned instead.
*
* \param[out] hasArealRoot : boolean true if a real root is found according to the
* absImaginaryThreshold criterion, false otherwise.
* \param[in] absImaginaryThreshold : threshold on the absolute imaginary part to decide
* whether or not a root is real.
*/
inline const RealScalar& absGreatestRealRoot(
bool& hasArealRoot, const RealScalar& absImaginaryThreshold = NumTraits<Scalar>::dummy_precision()) const {
std::greater<RealScalar> greater;
return selectRealRoot_withRespectToAbsRealPart(greater, hasArealRoot, absImaginaryThreshold);
}
/**
* \returns a real root with smallest absolute magnitude.
* A real root is defined as the real part of a complex root with absolute imaginary
* part smallest than absImaginaryThreshold.
* absImaginaryThreshold takes the dummy_precision associated
* with the Scalar_ template parameter of the PolynomialSolver class as the default value.
* If no real root is found the boolean hasArealRoot is set to false and the real part of
* the root with smallest absolute imaginary part is returned instead.
*
* \param[out] hasArealRoot : boolean true if a real root is found according to the
* absImaginaryThreshold criterion, false otherwise.
* \param[in] absImaginaryThreshold : threshold on the absolute imaginary part to decide
* whether or not a root is real.
*/
inline const RealScalar& absSmallestRealRoot(
bool& hasArealRoot, const RealScalar& absImaginaryThreshold = NumTraits<Scalar>::dummy_precision()) const {
std::less<RealScalar> less;
return selectRealRoot_withRespectToAbsRealPart(less, hasArealRoot, absImaginaryThreshold);
}
/**
* \returns the real root with greatest value.
* A real root is defined as the real part of a complex root with absolute imaginary
* part smallest than absImaginaryThreshold.
* absImaginaryThreshold takes the dummy_precision associated
* with the Scalar_ template parameter of the PolynomialSolver class as the default value.
* If no real root is found the boolean hasArealRoot is set to false and the real part of
* the root with smallest absolute imaginary part is returned instead.
*
* \param[out] hasArealRoot : boolean true if a real root is found according to the
* absImaginaryThreshold criterion, false otherwise.
* \param[in] absImaginaryThreshold : threshold on the absolute imaginary part to decide
* whether or not a root is real.
*/
inline const RealScalar& greatestRealRoot(
bool& hasArealRoot, const RealScalar& absImaginaryThreshold = NumTraits<Scalar>::dummy_precision()) const {
std::greater<RealScalar> greater;
return selectRealRoot_withRespectToRealPart(greater, hasArealRoot, absImaginaryThreshold);
}
/**
* \returns the real root with smallest value.
* A real root is defined as the real part of a complex root with absolute imaginary
* part smallest than absImaginaryThreshold.
* absImaginaryThreshold takes the dummy_precision associated
* with the Scalar_ template parameter of the PolynomialSolver class as the default value.
* If no real root is found the boolean hasArealRoot is set to false and the real part of
* the root with smallest absolute imaginary part is returned instead.
*
* \param[out] hasArealRoot : boolean true if a real root is found according to the
* absImaginaryThreshold criterion, false otherwise.
* \param[in] absImaginaryThreshold : threshold on the absolute imaginary part to decide
* whether or not a root is real.
*/
inline const RealScalar& smallestRealRoot(
bool& hasArealRoot, const RealScalar& absImaginaryThreshold = NumTraits<Scalar>::dummy_precision()) const {
std::less<RealScalar> less;
return selectRealRoot_withRespectToRealPart(less, hasArealRoot, absImaginaryThreshold);
}
protected:
RootsType m_roots;
};
#define EIGEN_POLYNOMIAL_SOLVER_BASE_INHERITED_TYPES(BASE) \
typedef typename BASE::Scalar Scalar; \
typedef typename BASE::RealScalar RealScalar; \
typedef typename BASE::RootType RootType; \
typedef typename BASE::RootsType RootsType;
/** \ingroup Polynomials_Module
*
* \class PolynomialSolver
*
* \brief A polynomial solver
*
* Computes the complex roots of a real polynomial.
*
* \param Scalar_ the scalar type, i.e., the type of the polynomial coefficients
* \param Deg_ the degree of the polynomial, can be a compile time value or Dynamic.
* Notice that the number of polynomial coefficients is Deg_+1.
*
* This class implements a polynomial solver and provides convenient methods such as
* - real roots,
* - greatest, smallest complex roots,
* - real roots with greatest, smallest absolute real value.
* - greatest, smallest real roots.
*
* WARNING: this polynomial solver is experimental, part of the unsupported Eigen modules.
*
*
* Currently a QR algorithm is used to compute the eigenvalues of the companion matrix of
* the polynomial to compute its roots.
* This supposes that the complex moduli of the roots are all distinct: e.g. there should
* be no multiple roots or conjugate roots for instance.
* With 32bit (float) floating types this problem shows up frequently.
* However, almost always, correct accuracy is reached even in these cases for 64bit
* (double) floating types and small polynomial degree (<20).
*/
template <typename Scalar_, int Deg_>
class PolynomialSolver : public PolynomialSolverBase<Scalar_, Deg_> {
public:
EIGEN_MAKE_ALIGNED_OPERATOR_NEW_IF_VECTORIZABLE_FIXED_SIZE(Scalar_, Deg_ == Dynamic ? Dynamic : Deg_)
typedef PolynomialSolverBase<Scalar_, Deg_> PS_Base;
EIGEN_POLYNOMIAL_SOLVER_BASE_INHERITED_TYPES(PS_Base)
typedef Matrix<Scalar, Deg_, Deg_> CompanionMatrixType;
typedef std::conditional_t<NumTraits<Scalar>::IsComplex, ComplexEigenSolver<CompanionMatrixType>,
EigenSolver<CompanionMatrixType> >
EigenSolverType;
typedef std::conditional_t<NumTraits<Scalar>::IsComplex, Scalar, std::complex<Scalar> > ComplexScalar;
public:
/** Computes the complex roots of a new polynomial. */
template <typename OtherPolynomial>
void compute(const OtherPolynomial& poly) {
eigen_assert(Scalar(0) != poly[poly.size() - 1]);
eigen_assert(poly.size() > 1);
if (poly.size() > 2) {
internal::companion<Scalar, Deg_> companion(poly);
companion.balance();
m_eigenSolver.compute(companion.denseMatrix());
eigen_assert(m_eigenSolver.info() == Eigen::Success);
m_roots = m_eigenSolver.eigenvalues();
// cleanup noise in imaginary part of real roots:
// if the imaginary part is rather small compared to the real part
// and that cancelling the imaginary part yield a smaller evaluation,
// then it's safe to keep the real part only.
RealScalar coarse_prec = RealScalar(std::pow(4, poly.size() + 1)) * NumTraits<RealScalar>::epsilon();
for (Index i = 0; i < m_roots.size(); ++i) {
if (internal::isMuchSmallerThan(numext::abs(numext::imag(m_roots[i])), numext::abs(numext::real(m_roots[i])),
coarse_prec)) {
ComplexScalar as_real_root = ComplexScalar(numext::real(m_roots[i]));
if (numext::abs(poly_eval(poly, as_real_root)) <= numext::abs(poly_eval(poly, m_roots[i]))) {
m_roots[i] = as_real_root;
}
}
}
} else if (poly.size() == 2) {
m_roots.resize(1);
m_roots[0] = -poly[0] / poly[1];
}
}
public:
template <typename OtherPolynomial>
inline PolynomialSolver(const OtherPolynomial& poly) {
compute(poly);
}
inline PolynomialSolver() {}
protected:
using PS_Base::m_roots;
EigenSolverType m_eigenSolver;
};
template <typename Scalar_>
class PolynomialSolver<Scalar_, 1> : public PolynomialSolverBase<Scalar_, 1> {
public:
typedef PolynomialSolverBase<Scalar_, 1> PS_Base;
EIGEN_POLYNOMIAL_SOLVER_BASE_INHERITED_TYPES(PS_Base)
public:
/** Computes the complex roots of a new polynomial. */
template <typename OtherPolynomial>
void compute(const OtherPolynomial& poly) {
eigen_assert(poly.size() == 2);
eigen_assert(Scalar(0) != poly[1]);
m_roots[0] = -poly[0] / poly[1];
}
public:
template <typename OtherPolynomial>
inline PolynomialSolver(const OtherPolynomial& poly) {
compute(poly);
}
inline PolynomialSolver() {}
protected:
using PS_Base::m_roots;
};
} // end namespace Eigen
#endif // EIGEN_POLYNOMIAL_SOLVER_H