• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
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 >
setPolynomial(const OtherPolynomial & poly)43     inline void setPolynomial( const OtherPolynomial& poly ){
44       m_roots.resize(poly.size()); }
45 
46   public:
47     template< typename OtherPolynomial >
PolynomialSolverBase(const OtherPolynomial & poly)48     inline PolynomialSolverBase( const OtherPolynomial& poly ){
49       setPolynomial( poly() ); }
50 
PolynomialSolverBase()51     inline PolynomialSolverBase(){}
52 
53   public:
54     /** \returns the complex roots of the polynomial */
roots()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>
selectComplexRoot_withRespectToNorm(squaredNormBinaryPredicate & pred)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      */
greatestRoot()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      */
smallestRoot()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 >
compute(const OtherPolynomial & poly)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 >
PolynomialSolver(const OtherPolynomial & poly)353     inline PolynomialSolver( const OtherPolynomial& poly ){
354       compute( poly ); }
355 
PolynomialSolver()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;
EIGEN_POLYNOMIAL_SOLVER_BASE_INHERITED_TYPES(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