• 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()-1); }
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       using std::abs;
73       bi_seq.clear();
74       for(Index i=0; i<m_roots.size(); ++i )
75       {
76         if( abs( m_roots[i].imag() ) < absImaginaryThreshold ){
77           bi_seq.push_back( m_roots[i].real() ); }
78       }
79     }
80 
81   protected:
82     template<typename squaredNormBinaryPredicate>
selectComplexRoot_withRespectToNorm(squaredNormBinaryPredicate & pred)83     inline const RootType& selectComplexRoot_withRespectToNorm( squaredNormBinaryPredicate& pred ) const
84     {
85       Index res=0;
86       RealScalar norm2 = numext::abs2( m_roots[0] );
87       for( Index i=1; i<m_roots.size(); ++i )
88       {
89         const RealScalar currNorm2 = numext::abs2( m_roots[i] );
90         if( pred( currNorm2, norm2 ) ){
91           res=i; norm2=currNorm2; }
92       }
93       return m_roots[res];
94     }
95 
96   public:
97     /**
98      * \returns the complex root with greatest norm.
99      */
greatestRoot()100     inline const RootType& greatestRoot() const
101     {
102       std::greater<RealScalar> greater;
103       return selectComplexRoot_withRespectToNorm( greater );
104     }
105 
106     /**
107      * \returns the complex root with smallest norm.
108      */
smallestRoot()109     inline const RootType& smallestRoot() const
110     {
111       std::less<RealScalar> less;
112       return selectComplexRoot_withRespectToNorm( less );
113     }
114 
115   protected:
116     template<typename squaredRealPartBinaryPredicate>
117     inline const RealScalar& selectRealRoot_withRespectToAbsRealPart(
118         squaredRealPartBinaryPredicate& pred,
119         bool& hasArealRoot,
120         const RealScalar& absImaginaryThreshold = NumTraits<Scalar>::dummy_precision() ) const
121     {
122       using std::abs;
123       hasArealRoot = false;
124       Index res=0;
125       RealScalar abs2(0);
126 
127       for( Index i=0; i<m_roots.size(); ++i )
128       {
129         if( abs( m_roots[i].imag() ) <= absImaginaryThreshold )
130         {
131           if( !hasArealRoot )
132           {
133             hasArealRoot = true;
134             res = i;
135             abs2 = m_roots[i].real() * m_roots[i].real();
136           }
137           else
138           {
139             const RealScalar currAbs2 = m_roots[i].real() * m_roots[i].real();
140             if( pred( currAbs2, abs2 ) )
141             {
142               abs2 = currAbs2;
143               res = i;
144             }
145           }
146         }
147         else if(!hasArealRoot)
148         {
149           if( abs( m_roots[i].imag() ) < abs( m_roots[res].imag() ) ){
150             res = i;}
151         }
152       }
153       return numext::real_ref(m_roots[res]);
154     }
155 
156 
157     template<typename RealPartBinaryPredicate>
158     inline const RealScalar& selectRealRoot_withRespectToRealPart(
159         RealPartBinaryPredicate& pred,
160         bool& hasArealRoot,
161         const RealScalar& absImaginaryThreshold = NumTraits<Scalar>::dummy_precision() ) const
162     {
163       using std::abs;
164       hasArealRoot = false;
165       Index res=0;
166       RealScalar val(0);
167 
168       for( Index i=0; i<m_roots.size(); ++i )
169       {
170         if( abs( m_roots[i].imag() ) <= absImaginaryThreshold )
171         {
172           if( !hasArealRoot )
173           {
174             hasArealRoot = true;
175             res = i;
176             val = m_roots[i].real();
177           }
178           else
179           {
180             const RealScalar curr = m_roots[i].real();
181             if( pred( curr, val ) )
182             {
183               val = curr;
184               res = i;
185             }
186           }
187         }
188         else
189         {
190           if( abs( m_roots[i].imag() ) < abs( m_roots[res].imag() ) ){
191             res = i; }
192         }
193       }
194       return numext::real_ref(m_roots[res]);
195     }
196 
197   public:
198     /**
199      * \returns a real root with greatest absolute magnitude.
200      * A real root is defined as the real part of a complex root with absolute imaginary
201      * part smallest than absImaginaryThreshold.
202      * absImaginaryThreshold takes the dummy_precision associated
203      * with the _Scalar template parameter of the PolynomialSolver class as the default value.
204      * If no real root is found the boolean hasArealRoot is set to false and the real part of
205      * the root with smallest absolute imaginary part is returned instead.
206      *
207      * \param[out] hasArealRoot : boolean true if a real root is found according to the
208      *  absImaginaryThreshold criterion, false otherwise.
209      * \param[in] absImaginaryThreshold : threshold on the absolute imaginary part to decide
210      *  whether or not a root is real.
211      */
212     inline const RealScalar& absGreatestRealRoot(
213         bool& hasArealRoot,
214         const RealScalar& absImaginaryThreshold = NumTraits<Scalar>::dummy_precision() ) const
215     {
216       std::greater<RealScalar> greater;
217       return selectRealRoot_withRespectToAbsRealPart( greater, hasArealRoot, absImaginaryThreshold );
218     }
219 
220 
221     /**
222      * \returns a real root with smallest absolute magnitude.
223      * A real root is defined as the real part of a complex root with absolute imaginary
224      * part smallest than absImaginaryThreshold.
225      * absImaginaryThreshold takes the dummy_precision associated
226      * with the _Scalar template parameter of the PolynomialSolver class as the default value.
227      * If no real root is found the boolean hasArealRoot is set to false and the real part of
228      * the root with smallest absolute imaginary part is returned instead.
229      *
230      * \param[out] hasArealRoot : boolean true if a real root is found according to the
231      *  absImaginaryThreshold criterion, false otherwise.
232      * \param[in] absImaginaryThreshold : threshold on the absolute imaginary part to decide
233      *  whether or not a root is real.
234      */
235     inline const RealScalar& absSmallestRealRoot(
236         bool& hasArealRoot,
237         const RealScalar& absImaginaryThreshold = NumTraits<Scalar>::dummy_precision() ) const
238     {
239       std::less<RealScalar> less;
240       return selectRealRoot_withRespectToAbsRealPart( less, hasArealRoot, absImaginaryThreshold );
241     }
242 
243 
244     /**
245      * \returns the real root with greatest value.
246      * A real root is defined as the real part of a complex root with absolute imaginary
247      * part smallest than absImaginaryThreshold.
248      * absImaginaryThreshold takes the dummy_precision associated
249      * with the _Scalar template parameter of the PolynomialSolver class as the default value.
250      * If no real root is found the boolean hasArealRoot is set to false and the real part of
251      * the root with smallest absolute imaginary part is returned instead.
252      *
253      * \param[out] hasArealRoot : boolean true if a real root is found according to the
254      *  absImaginaryThreshold criterion, false otherwise.
255      * \param[in] absImaginaryThreshold : threshold on the absolute imaginary part to decide
256      *  whether or not a root is real.
257      */
258     inline const RealScalar& greatestRealRoot(
259         bool& hasArealRoot,
260         const RealScalar& absImaginaryThreshold = NumTraits<Scalar>::dummy_precision() ) const
261     {
262       std::greater<RealScalar> greater;
263       return selectRealRoot_withRespectToRealPart( greater, hasArealRoot, absImaginaryThreshold );
264     }
265 
266 
267     /**
268      * \returns the real root with smallest value.
269      * A real root is defined as the real part of a complex root with absolute imaginary
270      * part smallest than absImaginaryThreshold.
271      * absImaginaryThreshold takes the dummy_precision associated
272      * with the _Scalar template parameter of the PolynomialSolver class as the default value.
273      * If no real root is found the boolean hasArealRoot is set to false and the real part of
274      * the root with smallest absolute imaginary part is returned instead.
275      *
276      * \param[out] hasArealRoot : boolean true if a real root is found according to the
277      *  absImaginaryThreshold criterion, false otherwise.
278      * \param[in] absImaginaryThreshold : threshold on the absolute imaginary part to decide
279      *  whether or not a root is real.
280      */
281     inline const RealScalar& smallestRealRoot(
282         bool& hasArealRoot,
283         const RealScalar& absImaginaryThreshold = NumTraits<Scalar>::dummy_precision() ) const
284     {
285       std::less<RealScalar> less;
286       return selectRealRoot_withRespectToRealPart( less, hasArealRoot, absImaginaryThreshold );
287     }
288 
289   protected:
290     RootsType               m_roots;
291 };
292 
293 #define EIGEN_POLYNOMIAL_SOLVER_BASE_INHERITED_TYPES( BASE )  \
294   typedef typename BASE::Scalar                 Scalar;       \
295   typedef typename BASE::RealScalar             RealScalar;   \
296   typedef typename BASE::RootType               RootType;     \
297   typedef typename BASE::RootsType              RootsType;
298 
299 
300 
301 /** \ingroup Polynomials_Module
302   *
303   * \class PolynomialSolver
304   *
305   * \brief A polynomial solver
306   *
307   * Computes the complex roots of a real polynomial.
308   *
309   * \param _Scalar the scalar type, i.e., the type of the polynomial coefficients
310   * \param _Deg the degree of the polynomial, can be a compile time value or Dynamic.
311   *             Notice that the number of polynomial coefficients is _Deg+1.
312   *
313   * This class implements a polynomial solver and provides convenient methods such as
314   * - real roots,
315   * - greatest, smallest complex roots,
316   * - real roots with greatest, smallest absolute real value.
317   * - greatest, smallest real roots.
318   *
319   * WARNING: this polynomial solver is experimental, part of the unsupported Eigen modules.
320   *
321   *
322   * Currently a QR algorithm is used to compute the eigenvalues of the companion matrix of
323   * the polynomial to compute its roots.
324   * This supposes that the complex moduli of the roots are all distinct: e.g. there should
325   * be no multiple roots or conjugate roots for instance.
326   * With 32bit (float) floating types this problem shows up frequently.
327   * However, almost always, correct accuracy is reached even in these cases for 64bit
328   * (double) floating types and small polynomial degree (<20).
329   */
330 template<typename _Scalar, int _Deg>
331 class PolynomialSolver : public PolynomialSolverBase<_Scalar,_Deg>
332 {
333   public:
334     EIGEN_MAKE_ALIGNED_OPERATOR_NEW_IF_VECTORIZABLE_FIXED_SIZE(_Scalar,_Deg==Dynamic ? Dynamic : _Deg)
335 
336     typedef PolynomialSolverBase<_Scalar,_Deg>    PS_Base;
337     EIGEN_POLYNOMIAL_SOLVER_BASE_INHERITED_TYPES( PS_Base )
338 
339     typedef Matrix<Scalar,_Deg,_Deg>                 CompanionMatrixType;
340     typedef typename internal::conditional<NumTraits<Scalar>::IsComplex,
341                                           ComplexEigenSolver<CompanionMatrixType>,
342                                           EigenSolver<CompanionMatrixType> >::type EigenSolverType;
343     typedef typename internal::conditional<NumTraits<Scalar>::IsComplex, Scalar, std::complex<Scalar> >::type ComplexScalar;
344 
345   public:
346     /** Computes the complex roots of a new polynomial. */
347     template< typename OtherPolynomial >
compute(const OtherPolynomial & poly)348     void compute( const OtherPolynomial& poly )
349     {
350       eigen_assert( Scalar(0) != poly[poly.size()-1] );
351       eigen_assert( poly.size() > 1 );
352       if(poly.size() >  2 )
353       {
354         internal::companion<Scalar,_Deg> companion( poly );
355         companion.balance();
356         m_eigenSolver.compute( companion.denseMatrix() );
357         m_roots = m_eigenSolver.eigenvalues();
358         // cleanup noise in imaginary part of real roots:
359         // if the imaginary part is rather small compared to the real part
360         // and that cancelling the imaginary part yield a smaller evaluation,
361         // then it's safe to keep the real part only.
362         RealScalar coarse_prec = RealScalar(std::pow(4,poly.size()+1))*NumTraits<RealScalar>::epsilon();
363         for(Index i = 0; i<m_roots.size(); ++i)
364         {
365           if( internal::isMuchSmallerThan(numext::abs(numext::imag(m_roots[i])),
366                                           numext::abs(numext::real(m_roots[i])),
367                                           coarse_prec) )
368           {
369             ComplexScalar as_real_root = ComplexScalar(numext::real(m_roots[i]));
370             if(    numext::abs(poly_eval(poly, as_real_root))
371                 <= numext::abs(poly_eval(poly, m_roots[i])))
372             {
373               m_roots[i] = as_real_root;
374             }
375           }
376         }
377       }
378       else if(poly.size () == 2)
379       {
380         m_roots.resize(1);
381         m_roots[0] = -poly[0]/poly[1];
382       }
383     }
384 
385   public:
386     template< typename OtherPolynomial >
PolynomialSolver(const OtherPolynomial & poly)387     inline PolynomialSolver( const OtherPolynomial& poly ){
388       compute( poly ); }
389 
PolynomialSolver()390     inline PolynomialSolver(){}
391 
392   protected:
393     using                   PS_Base::m_roots;
394     EigenSolverType         m_eigenSolver;
395 };
396 
397 
398 template< typename _Scalar >
399 class PolynomialSolver<_Scalar,1> : public PolynomialSolverBase<_Scalar,1>
400 {
401   public:
402     typedef PolynomialSolverBase<_Scalar,1>    PS_Base;
EIGEN_POLYNOMIAL_SOLVER_BASE_INHERITED_TYPES(PS_Base)403     EIGEN_POLYNOMIAL_SOLVER_BASE_INHERITED_TYPES( PS_Base )
404 
405   public:
406     /** Computes the complex roots of a new polynomial. */
407     template< typename OtherPolynomial >
408     void compute( const OtherPolynomial& poly )
409     {
410       eigen_assert( poly.size() == 2 );
411       eigen_assert( Scalar(0) != poly[1] );
412       m_roots[0] = -poly[0]/poly[1];
413     }
414 
415   public:
416     template< typename OtherPolynomial >
PolynomialSolver(const OtherPolynomial & poly)417     inline PolynomialSolver( const OtherPolynomial& poly ){
418       compute( poly ); }
419 
PolynomialSolver()420     inline PolynomialSolver(){}
421 
422   protected:
423     using                   PS_Base::m_roots;
424 };
425 
426 } // end namespace Eigen
427 
428 #endif // EIGEN_POLYNOMIAL_SOLVER_H
429