Home | History | Annotate | Download | only in Polynomials
      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 (at) 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 
     10 #ifndef EIGEN_POLYNOMIAL_SOLVER_H
     11 #define EIGEN_POLYNOMIAL_SOLVER_H
     12 
     13 namespace Eigen {
     14 
     15 /** \ingroup Polynomials_Module
     16  *  \class PolynomialSolverBase.
     17  *
     18  * \brief Defined to be inherited by polynomial solvers: it provides
     19  * convenient methods such as
     20  *  - real roots,
     21  *  - greatest, smallest complex roots,
     22  *  - real roots with greatest, smallest absolute real value,
     23  *  - greatest, smallest real roots.
     24  *
     25  * It stores the set of roots as a vector of complexes.
     26  *
     27  */
     28 template< typename _Scalar, int _Deg >
     29 class PolynomialSolverBase
     30 {
     31   public:
     32     EIGEN_MAKE_ALIGNED_OPERATOR_NEW_IF_VECTORIZABLE_FIXED_SIZE(_Scalar,_Deg==Dynamic ? Dynamic : _Deg)
     33 
     34     typedef _Scalar                             Scalar;
     35     typedef typename NumTraits<Scalar>::Real    RealScalar;
     36     typedef std::complex<RealScalar>            RootType;
     37     typedef Matrix<RootType,_Deg,1>             RootsType;
     38 
     39     typedef DenseIndex Index;
     40 
     41   protected:
     42     template< typename OtherPolynomial >
     43     inline void setPolynomial( const OtherPolynomial& poly ){
     44       m_roots.resize(poly.size()); }
     45 
     46   public:
     47     template< typename OtherPolynomial >
     48     inline PolynomialSolverBase( const OtherPolynomial& poly ){
     49       setPolynomial( poly() ); }
     50 
     51     inline PolynomialSolverBase(){}
     52 
     53   public:
     54     /** \returns the complex roots of the polynomial */
     55     inline const RootsType& roots() const { return m_roots; }
     56 
     57   public:
     58     /** Clear and fills the back insertion sequence with the real roots of the polynomial
     59      * i.e. the real part of the complex roots that have an imaginary part which
     60      * absolute value is smaller than absImaginaryThreshold.
     61      * absImaginaryThreshold takes the dummy_precision associated
     62      * with the _Scalar template parameter of the PolynomialSolver class as the default value.
     63      *
     64      * \param[out] bi_seq : the back insertion sequence (stl concept)
     65      * \param[in]  absImaginaryThreshold : the maximum bound of the imaginary part of a complex
     66      *  number that is considered as real.
     67      * */
     68     template<typename Stl_back_insertion_sequence>
     69     inline void realRoots( Stl_back_insertion_sequence& bi_seq,
     70         const RealScalar& absImaginaryThreshold = NumTraits<Scalar>::dummy_precision() ) const
     71     {
     72       bi_seq.clear();
     73       for(Index i=0; i<m_roots.size(); ++i )
     74       {
     75         if( internal::abs( m_roots[i].imag() ) < absImaginaryThreshold ){
     76           bi_seq.push_back( m_roots[i].real() ); }
     77       }
     78     }
     79 
     80   protected:
     81     template<typename squaredNormBinaryPredicate>
     82     inline const RootType& selectComplexRoot_withRespectToNorm( squaredNormBinaryPredicate& pred ) const
     83     {
     84       Index res=0;
     85       RealScalar norm2 = internal::abs2( m_roots[0] );
     86       for( Index i=1; i<m_roots.size(); ++i )
     87       {
     88         const RealScalar currNorm2 = internal::abs2( m_roots[i] );
     89         if( pred( currNorm2, norm2 ) ){
     90           res=i; norm2=currNorm2; }
     91       }
     92       return m_roots[res];
     93     }
     94 
     95   public:
     96     /**
     97      * \returns the complex root with greatest norm.
     98      */
     99     inline const RootType& greatestRoot() const
    100     {
    101       std::greater<Scalar> greater;
    102       return selectComplexRoot_withRespectToNorm( greater );
    103     }
    104 
    105     /**
    106      * \returns the complex root with smallest norm.
    107      */
    108     inline const RootType& smallestRoot() const
    109     {
    110       std::less<Scalar> less;
    111       return selectComplexRoot_withRespectToNorm( less );
    112     }
    113 
    114   protected:
    115     template<typename squaredRealPartBinaryPredicate>
    116     inline const RealScalar& selectRealRoot_withRespectToAbsRealPart(
    117         squaredRealPartBinaryPredicate& pred,
    118         bool& hasArealRoot,
    119         const RealScalar& absImaginaryThreshold = NumTraits<Scalar>::dummy_precision() ) const
    120     {
    121       hasArealRoot = false;
    122       Index res=0;
    123       RealScalar abs2(0);
    124 
    125       for( Index i=0; i<m_roots.size(); ++i )
    126       {
    127         if( internal::abs( m_roots[i].imag() ) < absImaginaryThreshold )
    128         {
    129           if( !hasArealRoot )
    130           {
    131             hasArealRoot = true;
    132             res = i;
    133             abs2 = m_roots[i].real() * m_roots[i].real();
    134           }
    135           else
    136           {
    137             const RealScalar currAbs2 = m_roots[i].real() * m_roots[i].real();
    138             if( pred( currAbs2, abs2 ) )
    139             {
    140               abs2 = currAbs2;
    141               res = i;
    142             }
    143           }
    144         }
    145         else
    146         {
    147           if( internal::abs( m_roots[i].imag() ) < internal::abs( m_roots[res].imag() ) ){
    148             res = i; }
    149         }
    150       }
    151       return internal::real_ref(m_roots[res]);
    152     }
    153 
    154 
    155     template<typename RealPartBinaryPredicate>
    156     inline const RealScalar& selectRealRoot_withRespectToRealPart(
    157         RealPartBinaryPredicate& pred,
    158         bool& hasArealRoot,
    159         const RealScalar& absImaginaryThreshold = NumTraits<Scalar>::dummy_precision() ) const
    160     {
    161       hasArealRoot = false;
    162       Index res=0;
    163       RealScalar val(0);
    164 
    165       for( Index i=0; i<m_roots.size(); ++i )
    166       {
    167         if( internal::abs( m_roots[i].imag() ) < absImaginaryThreshold )
    168         {
    169           if( !hasArealRoot )
    170           {
    171             hasArealRoot = true;
    172             res = i;
    173             val = m_roots[i].real();
    174           }
    175           else
    176           {
    177             const RealScalar curr = m_roots[i].real();
    178             if( pred( curr, val ) )
    179             {
    180               val = curr;
    181               res = i;
    182             }
    183           }
    184         }
    185         else
    186         {
    187           if( internal::abs( m_roots[i].imag() ) < internal::abs( m_roots[res].imag() ) ){
    188             res = i; }
    189         }
    190       }
    191       return internal::real_ref(m_roots[res]);
    192     }
    193 
    194   public:
    195     /**
    196      * \returns a real root with greatest absolute magnitude.
    197      * A real root is defined as the real part of a complex root with absolute imaginary
    198      * part smallest than absImaginaryThreshold.
    199      * absImaginaryThreshold takes the dummy_precision associated
    200      * with the _Scalar template parameter of the PolynomialSolver class as the default value.
    201      * If no real root is found the boolean hasArealRoot is set to false and the real part of
    202      * the root with smallest absolute imaginary part is returned instead.
    203      *
    204      * \param[out] hasArealRoot : boolean true if a real root is found according to the
    205      *  absImaginaryThreshold criterion, false otherwise.
    206      * \param[in] absImaginaryThreshold : threshold on the absolute imaginary part to decide
    207      *  whether or not a root is real.
    208      */
    209     inline const RealScalar& absGreatestRealRoot(
    210         bool& hasArealRoot,
    211         const RealScalar& absImaginaryThreshold = NumTraits<Scalar>::dummy_precision() ) const
    212     {
    213       std::greater<Scalar> greater;
    214       return selectRealRoot_withRespectToAbsRealPart( greater, hasArealRoot, absImaginaryThreshold );
    215     }
    216 
    217 
    218     /**
    219      * \returns a real root with smallest absolute magnitude.
    220      * A real root is defined as the real part of a complex root with absolute imaginary
    221      * part smallest than absImaginaryThreshold.
    222      * absImaginaryThreshold takes the dummy_precision associated
    223      * with the _Scalar template parameter of the PolynomialSolver class as the default value.
    224      * If no real root is found the boolean hasArealRoot is set to false and the real part of
    225      * the root with smallest absolute imaginary part is returned instead.
    226      *
    227      * \param[out] hasArealRoot : boolean true if a real root is found according to the
    228      *  absImaginaryThreshold criterion, false otherwise.
    229      * \param[in] absImaginaryThreshold : threshold on the absolute imaginary part to decide
    230      *  whether or not a root is real.
    231      */
    232     inline const RealScalar& absSmallestRealRoot(
    233         bool& hasArealRoot,
    234         const RealScalar& absImaginaryThreshold = NumTraits<Scalar>::dummy_precision() ) const
    235     {
    236       std::less<Scalar> less;
    237       return selectRealRoot_withRespectToAbsRealPart( less, hasArealRoot, absImaginaryThreshold );
    238     }
    239 
    240 
    241     /**
    242      * \returns the real root with greatest value.
    243      * A real root is defined as the real part of a complex root with absolute imaginary
    244      * part smallest than absImaginaryThreshold.
    245      * absImaginaryThreshold takes the dummy_precision associated
    246      * with the _Scalar template parameter of the PolynomialSolver class as the default value.
    247      * If no real root is found the boolean hasArealRoot is set to false and the real part of
    248      * the root with smallest absolute imaginary part is returned instead.
    249      *
    250      * \param[out] hasArealRoot : boolean true if a real root is found according to the
    251      *  absImaginaryThreshold criterion, false otherwise.
    252      * \param[in] absImaginaryThreshold : threshold on the absolute imaginary part to decide
    253      *  whether or not a root is real.
    254      */
    255     inline const RealScalar& greatestRealRoot(
    256         bool& hasArealRoot,
    257         const RealScalar& absImaginaryThreshold = NumTraits<Scalar>::dummy_precision() ) const
    258     {
    259       std::greater<Scalar> greater;
    260       return selectRealRoot_withRespectToRealPart( greater, hasArealRoot, absImaginaryThreshold );
    261     }
    262 
    263 
    264     /**
    265      * \returns the real root with smallest value.
    266      * A real root is defined as the real part of a complex root with absolute imaginary
    267      * part smallest than absImaginaryThreshold.
    268      * absImaginaryThreshold takes the dummy_precision associated
    269      * with the _Scalar template parameter of the PolynomialSolver class as the default value.
    270      * If no real root is found the boolean hasArealRoot is set to false and the real part of
    271      * the root with smallest absolute imaginary part is returned instead.
    272      *
    273      * \param[out] hasArealRoot : boolean true if a real root is found according to the
    274      *  absImaginaryThreshold criterion, false otherwise.
    275      * \param[in] absImaginaryThreshold : threshold on the absolute imaginary part to decide
    276      *  whether or not a root is real.
    277      */
    278     inline const RealScalar& smallestRealRoot(
    279         bool& hasArealRoot,
    280         const RealScalar& absImaginaryThreshold = NumTraits<Scalar>::dummy_precision() ) const
    281     {
    282       std::less<Scalar> less;
    283       return selectRealRoot_withRespectToRealPart( less, hasArealRoot, absImaginaryThreshold );
    284     }
    285 
    286   protected:
    287     RootsType               m_roots;
    288 };
    289 
    290 #define EIGEN_POLYNOMIAL_SOLVER_BASE_INHERITED_TYPES( BASE )  \
    291   typedef typename BASE::Scalar                 Scalar;       \
    292   typedef typename BASE::RealScalar             RealScalar;   \
    293   typedef typename BASE::RootType               RootType;     \
    294   typedef typename BASE::RootsType              RootsType;
    295 
    296 
    297 
    298 /** \ingroup Polynomials_Module
    299   *
    300   * \class PolynomialSolver
    301   *
    302   * \brief A polynomial solver
    303   *
    304   * Computes the complex roots of a real polynomial.
    305   *
    306   * \param _Scalar the scalar type, i.e., the type of the polynomial coefficients
    307   * \param _Deg the degree of the polynomial, can be a compile time value or Dynamic.
    308   *             Notice that the number of polynomial coefficients is _Deg+1.
    309   *
    310   * This class implements a polynomial solver and provides convenient methods such as
    311   * - real roots,
    312   * - greatest, smallest complex roots,
    313   * - real roots with greatest, smallest absolute real value.
    314   * - greatest, smallest real roots.
    315   *
    316   * WARNING: this polynomial solver is experimental, part of the unsuported Eigen modules.
    317   *
    318   *
    319   * Currently a QR algorithm is used to compute the eigenvalues of the companion matrix of
    320   * the polynomial to compute its roots.
    321   * This supposes that the complex moduli of the roots are all distinct: e.g. there should
    322   * be no multiple roots or conjugate roots for instance.
    323   * With 32bit (float) floating types this problem shows up frequently.
    324   * However, almost always, correct accuracy is reached even in these cases for 64bit
    325   * (double) floating types and small polynomial degree (<20).
    326   */
    327 template< typename _Scalar, int _Deg >
    328 class PolynomialSolver : public PolynomialSolverBase<_Scalar,_Deg>
    329 {
    330   public:
    331     EIGEN_MAKE_ALIGNED_OPERATOR_NEW_IF_VECTORIZABLE_FIXED_SIZE(_Scalar,_Deg==Dynamic ? Dynamic : _Deg)
    332 
    333     typedef PolynomialSolverBase<_Scalar,_Deg>    PS_Base;
    334     EIGEN_POLYNOMIAL_SOLVER_BASE_INHERITED_TYPES( PS_Base )
    335 
    336     typedef Matrix<Scalar,_Deg,_Deg>                 CompanionMatrixType;
    337     typedef EigenSolver<CompanionMatrixType>         EigenSolverType;
    338 
    339   public:
    340     /** Computes the complex roots of a new polynomial. */
    341     template< typename OtherPolynomial >
    342     void compute( const OtherPolynomial& poly )
    343     {
    344       assert( Scalar(0) != poly[poly.size()-1] );
    345       internal::companion<Scalar,_Deg> companion( poly );
    346       companion.balance();
    347       m_eigenSolver.compute( companion.denseMatrix() );
    348       m_roots = m_eigenSolver.eigenvalues();
    349     }
    350 
    351   public:
    352     template< typename OtherPolynomial >
    353     inline PolynomialSolver( const OtherPolynomial& poly ){
    354       compute( poly ); }
    355 
    356     inline PolynomialSolver(){}
    357 
    358   protected:
    359     using                   PS_Base::m_roots;
    360     EigenSolverType         m_eigenSolver;
    361 };
    362 
    363 
    364 template< typename _Scalar >
    365 class PolynomialSolver<_Scalar,1> : public PolynomialSolverBase<_Scalar,1>
    366 {
    367   public:
    368     typedef PolynomialSolverBase<_Scalar,1>    PS_Base;
    369     EIGEN_POLYNOMIAL_SOLVER_BASE_INHERITED_TYPES( PS_Base )
    370 
    371   public:
    372     /** Computes the complex roots of a new polynomial. */
    373     template< typename OtherPolynomial >
    374     void compute( const OtherPolynomial& poly )
    375     {
    376       assert( Scalar(0) != poly[poly.size()-1] );
    377       m_roots[0] = -poly[0]/poly[poly.size()-1];
    378     }
    379 
    380   protected:
    381     using                   PS_Base::m_roots;
    382 };
    383 
    384 } // end namespace Eigen
    385 
    386 #endif // EIGEN_POLYNOMIAL_SOLVER_H
    387