| // 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 |
| |
| #include "./InternalHeaderCheck.h" |
| |
| namespace Eigen { |
| |
| namespace internal { |
| |
| #ifndef EIGEN_PARSED_BY_DOXYGEN |
| |
| 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 |
| || !(numext::isfinite)(colNorm) || !(numext::isfinite)(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$ |
| const RealScalar radix = RealScalar(2); |
| const RealScalar radix2 = RealScalar(4); |
| |
| rowB = rowNorm / radix; |
| colB = RealScalar(1); |
| const RealScalar s = colNorm + rowNorm; |
| |
| // Find sigma s.t. rowNorm / 2 <= 2^(2*sigma) * colNorm |
| RealScalar scout = colNorm; |
| while (scout < rowB) |
| { |
| colB *= radix; |
| scout *= radix2; |
| } |
| |
| // We now have an upper-bound for sigma, try to lower it. |
| // Find sigma s.t. 2^(2*sigma) * colNorm / 2 < rowNorm |
| scout = colNorm * (colB / radix) * colB; // Avoid overflow. |
| while (scout >= rowNorm) |
| { |
| colB /= radix; |
| scout /= radix2; |
| } |
| |
| // This line is used to avoid insubstantial balancing. |
| if ((rowNorm + radix * scout) < 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 |