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_COMPANION_H
11 #define EIGEN_COMPANION_H
12 
13 // This file requires the user to include
14 // * Eigen/Core
15 // * Eigen/src/PolynomialSolver.h
16 
17 namespace Eigen {
18 
19 namespace internal {
20 
21 #ifndef EIGEN_PARSED_BY_DOXYGEN
22 
23 template <typename T>
radix()24 T radix(){ return 2; }
25 
26 template <typename T>
radix2()27 T radix2(){ return radix<T>()*radix<T>(); }
28 
29 template<int Size>
30 struct decrement_if_fixed_size
31 {
32   enum {
33     ret = (Size == Dynamic) ? Dynamic : Size-1 };
34 };
35 
36 #endif
37 
38 template< typename _Scalar, int _Deg >
39 class companion
40 {
41   public:
42     EIGEN_MAKE_ALIGNED_OPERATOR_NEW_IF_VECTORIZABLE_FIXED_SIZE(_Scalar,_Deg==Dynamic ? Dynamic : _Deg)
43 
44     enum {
45       Deg = _Deg,
46       Deg_1=decrement_if_fixed_size<Deg>::ret
47     };
48 
49     typedef _Scalar                                Scalar;
50     typedef typename NumTraits<Scalar>::Real       RealScalar;
51     typedef Matrix<Scalar, Deg, 1>                 RightColumn;
52     //typedef DiagonalMatrix< Scalar, Deg_1, Deg_1 > BottomLeftDiagonal;
53     typedef Matrix<Scalar, Deg_1, 1>               BottomLeftDiagonal;
54 
55     typedef Matrix<Scalar, Deg, Deg>               DenseCompanionMatrixType;
56     typedef Matrix< Scalar, _Deg, Deg_1 >          LeftBlock;
57     typedef Matrix< Scalar, Deg_1, Deg_1 >         BottomLeftBlock;
58     typedef Matrix< Scalar, 1, Deg_1 >             LeftBlockFirstRow;
59 
60     typedef DenseIndex Index;
61 
62   public:
operator()63     EIGEN_STRONG_INLINE const _Scalar operator()(Index row, Index col ) const
64     {
65       if( m_bl_diag.rows() > col )
66       {
67         if( 0 < row ){ return m_bl_diag[col]; }
68         else{ return 0; }
69       }
70       else{ return m_monic[row]; }
71     }
72 
73   public:
74     template<typename VectorType>
setPolynomial(const VectorType & poly)75     void setPolynomial( const VectorType& poly )
76     {
77       const Index deg = poly.size()-1;
78       m_monic = -1/poly[deg] * poly.head(deg);
79       //m_bl_diag.setIdentity( deg-1 );
80       m_bl_diag.setOnes(deg-1);
81     }
82 
83     template<typename VectorType>
companion(const VectorType & poly)84     companion( const VectorType& poly ){
85       setPolynomial( poly ); }
86 
87   public:
denseMatrix()88     DenseCompanionMatrixType denseMatrix() const
89     {
90       const Index deg   = m_monic.size();
91       const Index deg_1 = deg-1;
92       DenseCompanionMatrixType companion(deg,deg);
93       companion <<
94         ( LeftBlock(deg,deg_1)
95           << LeftBlockFirstRow::Zero(1,deg_1),
96           BottomLeftBlock::Identity(deg-1,deg-1)*m_bl_diag.asDiagonal() ).finished()
97         , m_monic;
98       return companion;
99     }
100 
101 
102 
103   protected:
104     /** Helper function for the balancing algorithm.
105      * \returns true if the row and the column, having colNorm and rowNorm
106      * as norms, are balanced, false otherwise.
107      * colB and rowB are repectively the multipliers for
108      * the column and the row in order to balance them.
109      * */
110     bool balanced( Scalar colNorm, Scalar rowNorm,
111         bool& isBalanced, Scalar& colB, Scalar& rowB );
112 
113     /** Helper function for the balancing algorithm.
114      * \returns true if the row and the column, having colNorm and rowNorm
115      * as norms, are balanced, false otherwise.
116      * colB and rowB are repectively the multipliers for
117      * the column and the row in order to balance them.
118      * */
119     bool balancedR( Scalar colNorm, Scalar rowNorm,
120         bool& isBalanced, Scalar& colB, Scalar& rowB );
121 
122   public:
123     /**
124      * Balancing algorithm from B. N. PARLETT and C. REINSCH (1969)
125      * "Balancing a matrix for calculation of eigenvalues and eigenvectors"
126      * adapted to the case of companion matrices.
127      * A matrix with non zero row and non zero column is balanced
128      * for a certain norm if the i-th row and the i-th column
129      * have same norm for all i.
130      */
131     void balance();
132 
133   protected:
134       RightColumn                m_monic;
135       BottomLeftDiagonal         m_bl_diag;
136 };
137 
138 
139 
140 template< typename _Scalar, int _Deg >
141 inline
balanced(Scalar colNorm,Scalar rowNorm,bool & isBalanced,Scalar & colB,Scalar & rowB)142 bool companion<_Scalar,_Deg>::balanced( Scalar colNorm, Scalar rowNorm,
143     bool& isBalanced, Scalar& colB, Scalar& rowB )
144 {
145   if( Scalar(0) == colNorm || Scalar(0) == rowNorm ){ return true; }
146   else
147   {
148     //To find the balancing coefficients, if the radix is 2,
149     //one finds \f$ \sigma \f$ such that
150     // \f$ 2^{2\sigma-1} < rowNorm / colNorm \le 2^{2\sigma+1} \f$
151     // then the balancing coefficient for the row is \f$ 1/2^{\sigma} \f$
152     // and the balancing coefficient for the column is \f$ 2^{\sigma} \f$
153     rowB = rowNorm / radix<Scalar>();
154     colB = Scalar(1);
155     const Scalar s = colNorm + rowNorm;
156 
157     while (colNorm < rowB)
158     {
159       colB *= radix<Scalar>();
160       colNorm *= radix2<Scalar>();
161     }
162 
163     rowB = rowNorm * radix<Scalar>();
164 
165     while (colNorm >= rowB)
166     {
167       colB /= radix<Scalar>();
168       colNorm /= radix2<Scalar>();
169     }
170 
171     //This line is used to avoid insubstantial balancing
172     if ((rowNorm + colNorm) < Scalar(0.95) * s * colB)
173     {
174       isBalanced = false;
175       rowB = Scalar(1) / colB;
176       return false;
177     }
178     else{
179       return true; }
180   }
181 }
182 
183 template< typename _Scalar, int _Deg >
184 inline
balancedR(Scalar colNorm,Scalar rowNorm,bool & isBalanced,Scalar & colB,Scalar & rowB)185 bool companion<_Scalar,_Deg>::balancedR( Scalar colNorm, Scalar rowNorm,
186     bool& isBalanced, Scalar& colB, Scalar& rowB )
187 {
188   if( Scalar(0) == colNorm || Scalar(0) == rowNorm ){ return true; }
189   else
190   {
191     /**
192      * Set the norm of the column and the row to the geometric mean
193      * of the row and column norm
194      */
195     const _Scalar q = colNorm/rowNorm;
196     if( !isApprox( q, _Scalar(1) ) )
197     {
198       rowB = sqrt( colNorm/rowNorm );
199       colB = Scalar(1)/rowB;
200 
201       isBalanced = false;
202       return false;
203     }
204     else{
205       return true; }
206   }
207 }
208 
209 
210 template< typename _Scalar, int _Deg >
balance()211 void companion<_Scalar,_Deg>::balance()
212 {
213   using std::abs;
214   EIGEN_STATIC_ASSERT( Deg == Dynamic || 1 < Deg, YOU_MADE_A_PROGRAMMING_MISTAKE );
215   const Index deg   = m_monic.size();
216   const Index deg_1 = deg-1;
217 
218   bool hasConverged=false;
219   while( !hasConverged )
220   {
221     hasConverged = true;
222     Scalar colNorm,rowNorm;
223     Scalar colB,rowB;
224 
225     //First row, first column excluding the diagonal
226     //==============================================
227     colNorm = abs(m_bl_diag[0]);
228     rowNorm = abs(m_monic[0]);
229 
230     //Compute balancing of the row and the column
231     if( !balanced( colNorm, rowNorm, hasConverged, colB, rowB ) )
232     {
233       m_bl_diag[0] *= colB;
234       m_monic[0] *= rowB;
235     }
236 
237     //Middle rows and columns excluding the diagonal
238     //==============================================
239     for( Index i=1; i<deg_1; ++i )
240     {
241       // column norm, excluding the diagonal
242       colNorm = abs(m_bl_diag[i]);
243 
244       // row norm, excluding the diagonal
245       rowNorm = abs(m_bl_diag[i-1]) + abs(m_monic[i]);
246 
247       //Compute balancing of the row and the column
248       if( !balanced( colNorm, rowNorm, hasConverged, colB, rowB ) )
249       {
250         m_bl_diag[i]   *= colB;
251         m_bl_diag[i-1] *= rowB;
252         m_monic[i]     *= rowB;
253       }
254     }
255 
256     //Last row, last column excluding the diagonal
257     //============================================
258     const Index ebl = m_bl_diag.size()-1;
259     VectorBlock<RightColumn,Deg_1> headMonic( m_monic, 0, deg_1 );
260     colNorm = headMonic.array().abs().sum();
261     rowNorm = abs( m_bl_diag[ebl] );
262 
263     //Compute balancing of the row and the column
264     if( !balanced( colNorm, rowNorm, hasConverged, colB, rowB ) )
265     {
266       headMonic      *= colB;
267       m_bl_diag[ebl] *= rowB;
268     }
269   }
270 }
271 
272 } // end namespace internal
273 
274 } // end namespace Eigen
275 
276 #endif // EIGEN_COMPANION_H
277