| // This file is part of Eigen, a lightweight C++ template library | |
| // for linear algebra. | |
| // | |
| // Copyright (C) 2010 Manuel Yguel <manuel.yguel@gmail.com> | |
| // | |
| // This Source Code Form is subject to the terms of the Mozilla | |
| // Public License v. 2.0. If a copy of the MPL was not distributed | |
| // with this file, You can obtain one at http://mozilla.org/MPL/2.0/. | |
| #ifndef EIGEN_COMPANION_H | |
| #define EIGEN_COMPANION_H | |
| // This file requires the user to include | |
| // * Eigen/Core | |
| // * Eigen/src/PolynomialSolver.h | |
| namespace Eigen { | |
| namespace internal { | |
| #ifndef EIGEN_PARSED_BY_DOXYGEN | |
| template <typename T> | |
| T radix(){ return 2; } | |
| template <typename T> | |
| T radix2(){ return radix<T>()*radix<T>(); } | |
| template<int Size> | |
| struct decrement_if_fixed_size | |
| { | |
| enum { | |
| ret = (Size == Dynamic) ? Dynamic : Size-1 }; | |
| }; | |
| #endif | |
| template< typename _Scalar, int _Deg > | |
| class companion | |
| { | |
| public: | |
| EIGEN_MAKE_ALIGNED_OPERATOR_NEW_IF_VECTORIZABLE_FIXED_SIZE(_Scalar,_Deg==Dynamic ? Dynamic : _Deg) | |
| enum { | |
| Deg = _Deg, | |
| Deg_1=decrement_if_fixed_size<Deg>::ret | |
| }; | |
| typedef _Scalar Scalar; | |
| typedef typename NumTraits<Scalar>::Real RealScalar; | |
| typedef Matrix<Scalar, Deg, 1> RightColumn; | |
| //typedef DiagonalMatrix< Scalar, Deg_1, Deg_1 > BottomLeftDiagonal; | |
| typedef Matrix<Scalar, Deg_1, 1> BottomLeftDiagonal; | |
| typedef Matrix<Scalar, Deg, Deg> DenseCompanionMatrixType; | |
| typedef Matrix< Scalar, _Deg, Deg_1 > LeftBlock; | |
| typedef Matrix< Scalar, Deg_1, Deg_1 > BottomLeftBlock; | |
| typedef Matrix< Scalar, 1, Deg_1 > LeftBlockFirstRow; | |
| typedef DenseIndex Index; | |
| public: | |
| EIGEN_STRONG_INLINE const _Scalar operator()(Index row, Index col ) const | |
| { | |
| if( m_bl_diag.rows() > col ) | |
| { | |
| if( 0 < row ){ return m_bl_diag[col]; } | |
| else{ return 0; } | |
| } | |
| else{ return m_monic[row]; } | |
| } | |
| public: | |
| template<typename VectorType> | |
| void setPolynomial( const VectorType& poly ) | |
| { | |
| const Index deg = poly.size()-1; | |
| m_monic = -poly.head(deg)/poly[deg]; | |
| m_bl_diag.setOnes(deg-1); | |
| } | |
| template<typename VectorType> | |
| companion( const VectorType& poly ){ | |
| setPolynomial( poly ); } | |
| public: | |
| DenseCompanionMatrixType denseMatrix() const | |
| { | |
| const Index deg = m_monic.size(); | |
| const Index deg_1 = deg-1; | |
| DenseCompanionMatrixType companMat(deg,deg); | |
| companMat << | |
| ( LeftBlock(deg,deg_1) | |
| << LeftBlockFirstRow::Zero(1,deg_1), | |
| BottomLeftBlock::Identity(deg-1,deg-1)*m_bl_diag.asDiagonal() ).finished() | |
| , m_monic; | |
| return companMat; | |
| } | |
| protected: | |
| /** Helper function for the balancing algorithm. | |
| * \returns true if the row and the column, having colNorm and rowNorm | |
| * as norms, are balanced, false otherwise. | |
| * colB and rowB are respectively the multipliers for | |
| * the column and the row in order to balance them. | |
| * */ | |
| bool balanced( RealScalar colNorm, RealScalar rowNorm, | |
| bool& isBalanced, RealScalar& colB, RealScalar& rowB ); | |
| /** Helper function for the balancing algorithm. | |
| * \returns true if the row and the column, having colNorm and rowNorm | |
| * as norms, are balanced, false otherwise. | |
| * colB and rowB are respectively the multipliers for | |
| * the column and the row in order to balance them. | |
| * */ | |
| bool balancedR( RealScalar colNorm, RealScalar rowNorm, | |
| bool& isBalanced, RealScalar& colB, RealScalar& rowB ); | |
| public: | |
| /** | |
| * Balancing algorithm from B. N. PARLETT and C. REINSCH (1969) | |
| * "Balancing a matrix for calculation of eigenvalues and eigenvectors" | |
| * adapted to the case of companion matrices. | |
| * A matrix with non zero row and non zero column is balanced | |
| * for a certain norm if the i-th row and the i-th column | |
| * have same norm for all i. | |
| */ | |
| void balance(); | |
| protected: | |
| RightColumn m_monic; | |
| BottomLeftDiagonal m_bl_diag; | |
| }; | |
| template< typename _Scalar, int _Deg > | |
| inline | |
| bool companion<_Scalar,_Deg>::balanced( RealScalar colNorm, RealScalar rowNorm, | |
| bool& isBalanced, RealScalar& colB, RealScalar& rowB ) | |
| { | |
| if( RealScalar(0) == colNorm || RealScalar(0) == rowNorm ){ return true; } | |
| else | |
| { | |
| //To find the balancing coefficients, if the radix is 2, | |
| //one finds \f$ \sigma \f$ such that | |
| // \f$ 2^{2\sigma-1} < rowNorm / colNorm \le 2^{2\sigma+1} \f$ | |
| // then the balancing coefficient for the row is \f$ 1/2^{\sigma} \f$ | |
| // and the balancing coefficient for the column is \f$ 2^{\sigma} \f$ | |
| rowB = rowNorm / radix<RealScalar>(); | |
| colB = RealScalar(1); | |
| const RealScalar s = colNorm + rowNorm; | |
| while (colNorm < rowB) | |
| { | |
| colB *= radix<RealScalar>(); | |
| colNorm *= radix2<RealScalar>(); | |
| } | |
| rowB = rowNorm * radix<RealScalar>(); | |
| while (colNorm >= rowB) | |
| { | |
| colB /= radix<RealScalar>(); | |
| colNorm /= radix2<RealScalar>(); | |
| } | |
| //This line is used to avoid insubstantial balancing | |
| if ((rowNorm + colNorm) < RealScalar(0.95) * s * colB) | |
| { | |
| isBalanced = false; | |
| rowB = RealScalar(1) / colB; | |
| return false; | |
| } | |
| else{ | |
| return true; } | |
| } | |
| } | |
| template< typename _Scalar, int _Deg > | |
| inline | |
| bool companion<_Scalar,_Deg>::balancedR( RealScalar colNorm, RealScalar rowNorm, | |
| bool& isBalanced, RealScalar& colB, RealScalar& rowB ) | |
| { | |
| if( RealScalar(0) == colNorm || RealScalar(0) == rowNorm ){ return true; } | |
| else | |
| { | |
| /** | |
| * Set the norm of the column and the row to the geometric mean | |
| * of the row and column norm | |
| */ | |
| const RealScalar q = colNorm/rowNorm; | |
| if( !isApprox( q, _Scalar(1) ) ) | |
| { | |
| rowB = sqrt( colNorm/rowNorm ); | |
| colB = RealScalar(1)/rowB; | |
| isBalanced = false; | |
| return false; | |
| } | |
| else{ | |
| return true; } | |
| } | |
| } | |
| template< typename _Scalar, int _Deg > | |
| void companion<_Scalar,_Deg>::balance() | |
| { | |
| using std::abs; | |
| EIGEN_STATIC_ASSERT( Deg == Dynamic || 1 < Deg, YOU_MADE_A_PROGRAMMING_MISTAKE ); | |
| const Index deg = m_monic.size(); | |
| const Index deg_1 = deg-1; | |
| bool hasConverged=false; | |
| while( !hasConverged ) | |
| { | |
| hasConverged = true; | |
| RealScalar colNorm,rowNorm; | |
| RealScalar colB,rowB; | |
| //First row, first column excluding the diagonal | |
| //============================================== | |
| colNorm = abs(m_bl_diag[0]); | |
| rowNorm = abs(m_monic[0]); | |
| //Compute balancing of the row and the column | |
| if( !balanced( colNorm, rowNorm, hasConverged, colB, rowB ) ) | |
| { | |
| m_bl_diag[0] *= colB; | |
| m_monic[0] *= rowB; | |
| } | |
| //Middle rows and columns excluding the diagonal | |
| //============================================== | |
| for( Index i=1; i<deg_1; ++i ) | |
| { | |
| // column norm, excluding the diagonal | |
| colNorm = abs(m_bl_diag[i]); | |
| // row norm, excluding the diagonal | |
| rowNorm = abs(m_bl_diag[i-1]) + abs(m_monic[i]); | |
| //Compute balancing of the row and the column | |
| if( !balanced( colNorm, rowNorm, hasConverged, colB, rowB ) ) | |
| { | |
| m_bl_diag[i] *= colB; | |
| m_bl_diag[i-1] *= rowB; | |
| m_monic[i] *= rowB; | |
| } | |
| } | |
| //Last row, last column excluding the diagonal | |
| //============================================ | |
| const Index ebl = m_bl_diag.size()-1; | |
| VectorBlock<RightColumn,Deg_1> headMonic( m_monic, 0, deg_1 ); | |
| colNorm = headMonic.array().abs().sum(); | |
| rowNorm = abs( m_bl_diag[ebl] ); | |
| //Compute balancing of the row and the column | |
| if( !balanced( colNorm, rowNorm, hasConverged, colB, rowB ) ) | |
| { | |
| headMonic *= colB; | |
| m_bl_diag[ebl] *= rowB; | |
| } | |
| } | |
| } | |
| } // end namespace internal | |
| } // end namespace Eigen | |
| #endif // EIGEN_COMPANION_H |