PolynomialSolver.h revision c981c48f5bc9aefeffc0bcb0cc3934c2fae179dd
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
10#ifndef EIGEN_POLYNOMIAL_SOLVER_H
11#define EIGEN_POLYNOMIAL_SOLVER_H
12
13namespace 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 */
28template< typename _Scalar, int _Deg >
29class 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  */
327template< typename _Scalar, int _Deg >
328class 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
364template< typename _Scalar >
365class 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