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