mirror of
https://gitlab.com/libeigen/eigen.git
synced 2026-04-10 11:34:33 +08:00
* merge with mainline
* adapt Eigenvalues module to the new rule that the RowMajorBit must have the proper value for vectors * Fix RowMajorBit in ei_traits<ProductBase> * Fix vectorizability logic in CoeffBasedProduct
This commit is contained in:
@@ -295,8 +295,7 @@ template<typename Derived>
|
||||
bool LLT<MatrixType,_UpLo>::solveInPlace(MatrixBase<Derived> &bAndX) const
|
||||
{
|
||||
ei_assert(m_isInitialized && "LLT is not initialized.");
|
||||
const int size = m_matrix.rows();
|
||||
ei_assert(size==bAndX.rows());
|
||||
ei_assert(m_matrix.rows()==bAndX.rows());
|
||||
matrixL().solveInPlace(bAndX);
|
||||
matrixU().solveInPlace(bAndX);
|
||||
return true;
|
||||
|
||||
@@ -528,7 +528,7 @@ template<typename Derived> class DenseBase
|
||||
#endif
|
||||
|
||||
// disable the use of evalTo for dense objects with a nice compilation error
|
||||
template<typename Dest> inline void evalTo(Dest& dst) const
|
||||
template<typename Dest> inline void evalTo(Dest& ) const
|
||||
{
|
||||
EIGEN_STATIC_ASSERT((ei_is_same_type<Dest,void>::ret),THE_EVAL_EVALTO_FUNCTION_SHOULD_NEVER_BE_CALLED_FOR_DENSE_OBJECTS);
|
||||
}
|
||||
|
||||
@@ -274,7 +274,7 @@ template<typename Scalar, typename NewType>
|
||||
struct ei_scalar_cast_op {
|
||||
EIGEN_EMPTY_STRUCT_CTOR(ei_scalar_cast_op)
|
||||
typedef NewType result_type;
|
||||
EIGEN_STRONG_INLINE const NewType operator() (const Scalar& a) const { return static_cast<NewType>(a); }
|
||||
EIGEN_STRONG_INLINE const NewType operator() (const Scalar& a) const { return ei_cast<Scalar, NewType>(a); }
|
||||
};
|
||||
template<typename Scalar, typename NewType>
|
||||
struct ei_functor_traits<ei_scalar_cast_op<Scalar,NewType> >
|
||||
|
||||
@@ -126,6 +126,16 @@ DenseBase<Derived>::format(const IOFormat& fmt) const
|
||||
return WithFormat<Derived>(derived(), fmt);
|
||||
}
|
||||
|
||||
template<typename Scalar>
|
||||
struct ei_significant_decimals_impl
|
||||
{
|
||||
typedef typename NumTraits<Scalar>::Real RealScalar;
|
||||
static inline int run()
|
||||
{
|
||||
return ei_cast<RealScalar,int>(std::ceil(-ei_log(NumTraits<RealScalar>::epsilon())/ei_log(RealScalar(10))));
|
||||
}
|
||||
};
|
||||
|
||||
/** \internal
|
||||
* print the matrix \a _m to the output stream \a s using the output format \a fmt */
|
||||
template<typename Derived>
|
||||
@@ -145,9 +155,7 @@ std::ostream & ei_print_matrix(std::ostream & s, const Derived& _m, const IOForm
|
||||
{
|
||||
if (NumTraits<Scalar>::HasFloatingPoint)
|
||||
{
|
||||
typedef typename NumTraits<Scalar>::Real RealScalar;
|
||||
RealScalar explicit_precision_fp = std::ceil(-ei_log(NumTraits<Scalar>::epsilon())/ei_log(10.0));
|
||||
explicit_precision = static_cast<std::streamsize>(explicit_precision_fp);
|
||||
explicit_precision = ei_significant_decimals_impl<Scalar>::run();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -44,6 +44,23 @@ template<typename T> inline typename NumTraits<T>::Real ei_hypot(T x, T y)
|
||||
return p * ei_sqrt(T(1) + qp*qp);
|
||||
}
|
||||
|
||||
// the point of wrapping these casts in this helper template struct is to allow users to specialize it to custom types
|
||||
// that may not have the needed conversion operators (especially as c++98 doesn't have explicit conversion operators).
|
||||
|
||||
template<typename OldType, typename NewType> struct ei_cast_impl
|
||||
{
|
||||
static inline NewType run(const OldType& x)
|
||||
{
|
||||
return static_cast<NewType>(x);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename OldType, typename NewType> inline NewType ei_cast(const OldType& x)
|
||||
{
|
||||
return ei_cast_impl<OldType, NewType>::run(x);
|
||||
}
|
||||
|
||||
|
||||
/**************
|
||||
*** int ***
|
||||
**************/
|
||||
|
||||
@@ -42,7 +42,7 @@ struct ei_traits<ProductBase<Derived,_Lhs,_Rhs> > //: ei_traits<typename ei_clea
|
||||
ColsAtCompileTime = ei_traits<Rhs>::ColsAtCompileTime,
|
||||
MaxRowsAtCompileTime = ei_traits<Lhs>::MaxRowsAtCompileTime,
|
||||
MaxColsAtCompileTime = ei_traits<Rhs>::MaxColsAtCompileTime,
|
||||
Flags = (RowsAtCompileTime==1 ? RowMajorBit : 0)
|
||||
Flags = (MaxRowsAtCompileTime==1 ? RowMajorBit : 0)
|
||||
| EvalBeforeNestingBit | EvalBeforeAssigningBit | NestByRefBit,
|
||||
// Note that EvalBeforeNestingBit and NestByRefBit
|
||||
// are not used in practice because ei_nested is overloaded for products
|
||||
|
||||
@@ -369,10 +369,14 @@ static EIGEN_DONT_INLINE EIGEN_UNUSED Packet4f ei_pcos(Packet4f x)
|
||||
// For detail see here: http://www.beyond3d.com/content/articles/8/
|
||||
static EIGEN_UNUSED Packet4f ei_psqrt(Packet4f _x)
|
||||
{
|
||||
Packet4f half = ei_pmul(_x, ei_pset1(.5f));
|
||||
Packet4f x = _mm_rsqrt_ps(_x);
|
||||
x = ei_pmul(x, ei_psub(ei_pset1(1.5f), ei_pmul(half, ei_pmul(x,x))));
|
||||
return ei_pmul(_x,x);
|
||||
Packet4f half = ei_pmul(_x, ei_pset1(.5f));
|
||||
|
||||
/* select only the inverse sqrt of non-zero inputs */
|
||||
Packet4f non_zero_mask = _mm_cmpgt_ps(_x, ei_pset1(std::numeric_limits<float>::epsilon()));
|
||||
Packet4f x = _mm_and_ps(non_zero_mask, _mm_rsqrt_ps(_x));
|
||||
|
||||
x = ei_pmul(x, ei_psub(ei_pset1(1.5f), ei_pmul(half, ei_pmul(x,x))));
|
||||
return ei_pmul(_x,x);
|
||||
}
|
||||
|
||||
#endif // EIGEN_MATH_FUNCTIONS_SSE_H
|
||||
|
||||
@@ -72,10 +72,18 @@ struct ei_traits<CoeffBasedProduct<LhsNested,RhsNested,NestingFlags> >
|
||||
RhsRowMajor = RhsFlags & RowMajorBit,
|
||||
|
||||
CanVectorizeRhs = RhsRowMajor && (RhsFlags & PacketAccessBit)
|
||||
&& (ColsAtCompileTime == Dynamic || (ColsAtCompileTime % ei_packet_traits<Scalar>::size) == 0),
|
||||
&& (ColsAtCompileTime == Dynamic
|
||||
|| ( (ColsAtCompileTime % ei_packet_traits<Scalar>::size) == 0
|
||||
&& (RhsFlags&AlignedBit)
|
||||
)
|
||||
),
|
||||
|
||||
CanVectorizeLhs = (!LhsRowMajor) && (LhsFlags & PacketAccessBit)
|
||||
&& (RowsAtCompileTime == Dynamic || (RowsAtCompileTime % ei_packet_traits<Scalar>::size) == 0),
|
||||
&& (RowsAtCompileTime == Dynamic
|
||||
|| ( (RowsAtCompileTime % ei_packet_traits<Scalar>::size) == 0
|
||||
&& (LhsFlags&AlignedBit)
|
||||
)
|
||||
),
|
||||
|
||||
EvalToRowMajor = (MaxRowsAtCompileTime==1&&MaxColsAtCompileTime!=1) ? 1
|
||||
: (MaxColsAtCompileTime==1&&MaxRowsAtCompileTime!=1) ? 0
|
||||
@@ -84,8 +92,7 @@ struct ei_traits<CoeffBasedProduct<LhsNested,RhsNested,NestingFlags> >
|
||||
Flags = ((unsigned int)(LhsFlags | RhsFlags) & HereditaryBits & ~RowMajorBit)
|
||||
| (EvalToRowMajor ? RowMajorBit : 0)
|
||||
| NestingFlags
|
||||
| (CanVectorizeLhs || CanVectorizeRhs ? PacketAccessBit : 0)
|
||||
| (LhsFlags & RhsFlags & AlignedBit),
|
||||
| (CanVectorizeLhs || CanVectorizeRhs ? PacketAccessBit : 0),
|
||||
|
||||
CoeffReadCost = InnerSize == Dynamic ? Dynamic
|
||||
: InnerSize * (NumTraits<Scalar>::MulCost + LhsCoeffReadCost + RhsCoeffReadCost)
|
||||
@@ -96,8 +103,11 @@ struct ei_traits<CoeffBasedProduct<LhsNested,RhsNested,NestingFlags> >
|
||||
* loop of the product might be vectorized. This is the meaning of CanVectorizeInner. Since it doesn't affect
|
||||
* the Flags, it is safe to make this value depend on ActualPacketAccessBit, that doesn't affect the ABI.
|
||||
*/
|
||||
CanVectorizeInner = LhsRowMajor && (!RhsRowMajor) && (LhsFlags & RhsFlags & ActualPacketAccessBit)
|
||||
&& (InnerSize % ei_packet_traits<Scalar>::size == 0)
|
||||
CanVectorizeInner = LhsRowMajor
|
||||
&& (!RhsRowMajor)
|
||||
&& (LhsFlags & RhsFlags & ActualPacketAccessBit)
|
||||
&& (LhsFlags & RhsFlags & AlignedBit)
|
||||
&& (InnerSize % ei_packet_traits<Scalar>::size == 0)
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -31,9 +31,24 @@
|
||||
*
|
||||
* \class ComplexEigenSolver
|
||||
*
|
||||
* \brief Eigen values/vectors solver for general complex matrices
|
||||
* \brief Computes eigenvalues and eigenvectors of general complex matrices
|
||||
*
|
||||
* \param MatrixType the type of the matrix of which we are computing the eigen decomposition
|
||||
* \tparam _MatrixType the type of the matrix of which we are
|
||||
* computing the eigendecomposition; this is expected to be an
|
||||
* instantiation of the Matrix class template.
|
||||
*
|
||||
* The eigenvalues and eigenvectors of a matrix \f$ A \f$ are scalars
|
||||
* \f$ \lambda \f$ and vectors \f$ v \f$ such that \f$ Av = \lambda v
|
||||
* \f$. If \f$ D \f$ is a diagonal matrix with the eigenvalues on
|
||||
* the diagonal, and \f$ V \f$ is a matrix with the eigenvectors as
|
||||
* its columns, then \f$ A V = V D \f$. The matrix \f$ V \f$ is
|
||||
* almost always invertible, in which case we have \f$ A = V D V^{-1}
|
||||
* \f$. This is called the eigendecomposition.
|
||||
*
|
||||
* The main function in this class is compute(), which computes the
|
||||
* eigenvalues and eigenvectors of a given function. The
|
||||
* documentation for that function contains an example showing the
|
||||
* main features of the class.
|
||||
*
|
||||
* \sa class EigenSolver, class SelfAdjointEigenSolver
|
||||
*/
|
||||
@@ -48,21 +63,47 @@ template<typename _MatrixType> class ComplexEigenSolver
|
||||
MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime,
|
||||
MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime
|
||||
};
|
||||
|
||||
/** \brief Scalar type for matrices of type \p _MatrixType. */
|
||||
typedef typename MatrixType::Scalar Scalar;
|
||||
typedef typename NumTraits<Scalar>::Real RealScalar;
|
||||
typedef std::complex<RealScalar> Complex;
|
||||
typedef typename ei_plain_col_type<MatrixType, Complex>::type EigenvalueType;
|
||||
typedef Matrix<Complex, RowsAtCompileTime, ColsAtCompileTime, Options, MaxRowsAtCompileTime, ColsAtCompileTime> EigenvectorType;
|
||||
|
||||
/**
|
||||
* \brief Default Constructor.
|
||||
*
|
||||
* The default constructor is useful in cases in which the user intends to
|
||||
* perform decompositions via ComplexEigenSolver::compute(const MatrixType&).
|
||||
*/
|
||||
/** \brief Complex scalar type for \p _MatrixType.
|
||||
*
|
||||
* This is \c std::complex<Scalar> if #Scalar is real (e.g.,
|
||||
* \c float or \c double) and just \c Scalar if #Scalar is
|
||||
* complex.
|
||||
*/
|
||||
typedef std::complex<RealScalar> ComplexScalar;
|
||||
|
||||
/** \brief Type for vector of eigenvalues as returned by eigenvalues().
|
||||
*
|
||||
* This is a column vector with entries of type #ComplexScalar.
|
||||
* The length of the vector is the size of \p _MatrixType.
|
||||
*/
|
||||
typedef Matrix<ComplexScalar, ColsAtCompileTime, 1, Options, MaxColsAtCompileTime, 1> EigenvalueType;
|
||||
|
||||
/** \brief Type for matrix of eigenvectors as returned by eigenvectors().
|
||||
*
|
||||
* This is a square matrix with entries of type #ComplexScalar.
|
||||
* The size is the same as the size of \p _MatrixType.
|
||||
*/
|
||||
typedef Matrix<ComplexScalar, RowsAtCompileTime, ColsAtCompileTime, Options, MaxRowsAtCompileTime, ColsAtCompileTime> EigenvectorType;
|
||||
|
||||
/** \brief Default constructor.
|
||||
*
|
||||
* The default constructor is useful in cases in which the user intends to
|
||||
* perform decompositions via compute().
|
||||
*/
|
||||
ComplexEigenSolver() : m_eivec(), m_eivalues(), m_isInitialized(false)
|
||||
{}
|
||||
|
||||
/** \brief Constructor; computes eigendecomposition of given matrix.
|
||||
*
|
||||
* \param[in] matrix Square matrix whose eigendecomposition is to be computed.
|
||||
*
|
||||
* This constructor calls compute() to compute the eigendecomposition.
|
||||
*/
|
||||
ComplexEigenSolver(const MatrixType& matrix)
|
||||
: m_eivec(matrix.rows(),matrix.cols()),
|
||||
m_eivalues(matrix.cols()),
|
||||
@@ -71,22 +112,72 @@ template<typename _MatrixType> class ComplexEigenSolver
|
||||
compute(matrix);
|
||||
}
|
||||
|
||||
EigenvectorType eigenvectors(void) const
|
||||
/** \brief Returns the eigenvectors of given matrix.
|
||||
*
|
||||
* It is assumed that either the constructor
|
||||
* ComplexEigenSolver(const MatrixType& matrix) or the member
|
||||
* function compute(const MatrixType& matrix) has been called
|
||||
* before to compute the eigendecomposition of a matrix. This
|
||||
* function returns a matrix whose columns are the
|
||||
* eigenvectors. Column \f$ k \f$ is an eigenvector
|
||||
* corresponding to eigenvalue number \f$ k \f$ as returned by
|
||||
* eigenvalues(). The eigenvectors are normalized to have
|
||||
* (Euclidean) norm equal to one. The matrix returned by this
|
||||
* function is the matrix \f$ V \f$ in the eigendecomposition \f$
|
||||
* A = V D V^{-1} \f$, if it exists.
|
||||
*
|
||||
* Example: \include ComplexEigenSolver_eigenvectors.cpp
|
||||
* Output: \verbinclude ComplexEigenSolver_eigenvectors.out
|
||||
*/
|
||||
EigenvectorType eigenvectors() const
|
||||
{
|
||||
ei_assert(m_isInitialized && "ComplexEigenSolver is not initialized.");
|
||||
return m_eivec;
|
||||
}
|
||||
|
||||
/** \brief Returns the eigenvalues of given matrix.
|
||||
*
|
||||
* It is assumed that either the constructor
|
||||
* ComplexEigenSolver(const MatrixType& matrix) or the member
|
||||
* function compute(const MatrixType& matrix) has been called
|
||||
* before to compute the eigendecomposition of a matrix. This
|
||||
* function returns a column vector containing the
|
||||
* eigenvalues. Eigenvalues are repeated according to their
|
||||
* algebraic multiplicity, so there are as many eigenvalues as
|
||||
* rows in the matrix.
|
||||
*
|
||||
* Example: \include ComplexEigenSolver_eigenvalues.cpp
|
||||
* Output: \verbinclude ComplexEigenSolver_eigenvalues.out
|
||||
*/
|
||||
EigenvalueType eigenvalues() const
|
||||
{
|
||||
ei_assert(m_isInitialized && "ComplexEigenSolver is not initialized.");
|
||||
return m_eivalues;
|
||||
}
|
||||
|
||||
/** \brief Computes eigendecomposition of given matrix.
|
||||
*
|
||||
* \param[in] matrix Square matrix whose eigendecomposition is to be computed.
|
||||
*
|
||||
* This function computes the eigenvalues and eigenvectors of \p
|
||||
* matrix. The eigenvalues() and eigenvectors() functions can be
|
||||
* used to retrieve the computed eigendecomposition.
|
||||
*
|
||||
* The matrix is first reduced to Schur form using the
|
||||
* ComplexSchur class. The Schur decomposition is then used to
|
||||
* compute the eigenvalues and eigenvectors.
|
||||
*
|
||||
* The cost of the computation is dominated by the cost of the
|
||||
* Schur decomposition, which is \f$ O(n^3) \f$ where \f$ n \f$
|
||||
* is the size of the matrix.
|
||||
*
|
||||
* Example: \include ComplexEigenSolver_compute.cpp
|
||||
* Output: \verbinclude ComplexEigenSolver_compute.out
|
||||
*/
|
||||
void compute(const MatrixType& matrix);
|
||||
|
||||
protected:
|
||||
MatrixType m_eivec;
|
||||
EigenvectorType m_eivec;
|
||||
EigenvalueType m_eivalues;
|
||||
bool m_isInitialized;
|
||||
};
|
||||
@@ -97,56 +188,56 @@ void ComplexEigenSolver<MatrixType>::compute(const MatrixType& matrix)
|
||||
{
|
||||
// this code is inspired from Jampack
|
||||
assert(matrix.cols() == matrix.rows());
|
||||
int n = matrix.cols();
|
||||
m_eivalues.resize(n,1);
|
||||
m_eivec.resize(n,n);
|
||||
const int n = matrix.cols();
|
||||
const RealScalar matrixnorm = matrix.norm();
|
||||
|
||||
RealScalar eps = NumTraits<RealScalar>::epsilon();
|
||||
|
||||
// Reduce to complex Schur form
|
||||
// Step 1: Do a complex Schur decomposition, A = U T U^*
|
||||
// The eigenvalues are on the diagonal of T.
|
||||
ComplexSchur<MatrixType> schur(matrix);
|
||||
|
||||
m_eivalues = schur.matrixT().diagonal();
|
||||
|
||||
m_eivec.setZero();
|
||||
|
||||
Scalar d2, z;
|
||||
RealScalar norm = matrix.norm();
|
||||
|
||||
// compute the (normalized) eigenvectors
|
||||
// Step 2: Compute X such that T = X D X^(-1), where D is the diagonal of T.
|
||||
// The matrix X is unit triangular.
|
||||
EigenvectorType X = EigenvectorType::Zero(n, n);
|
||||
for(int k=n-1 ; k>=0 ; k--)
|
||||
{
|
||||
d2 = schur.matrixT().coeff(k,k);
|
||||
m_eivec.coeffRef(k,k) = Scalar(1.0,0.0);
|
||||
X.coeffRef(k,k) = ComplexScalar(1.0,0.0);
|
||||
// Compute X(i,k) using the (i,k) entry of the equation X T = D X
|
||||
for(int i=k-1 ; i>=0 ; i--)
|
||||
{
|
||||
m_eivec.coeffRef(i,k) = -schur.matrixT().coeff(i,k);
|
||||
X.coeffRef(i,k) = -schur.matrixT().coeff(i,k);
|
||||
if(k-i-1>0)
|
||||
m_eivec.coeffRef(i,k) -= (schur.matrixT().row(i).segment(i+1,k-i-1) * m_eivec.col(k).segment(i+1,k-i-1)).value();
|
||||
z = schur.matrixT().coeff(i,i) - d2;
|
||||
if(z==Scalar(0))
|
||||
ei_real_ref(z) = eps * norm;
|
||||
m_eivec.coeffRef(i,k) = m_eivec.coeff(i,k) / z;
|
||||
|
||||
X.coeffRef(i,k) -= (schur.matrixT().row(i).segment(i+1,k-i-1) * X.col(k).segment(i+1,k-i-1)).value();
|
||||
ComplexScalar z = schur.matrixT().coeff(i,i) - schur.matrixT().coeff(k,k);
|
||||
if(z==ComplexScalar(0))
|
||||
{
|
||||
// If the i-th and k-th eigenvalue are equal, then z equals 0.
|
||||
// Use a small value instead, to prevent division by zero.
|
||||
ei_real_ref(z) = NumTraits<RealScalar>::epsilon() * matrixnorm;
|
||||
}
|
||||
X.coeffRef(i,k) = X.coeff(i,k) / z;
|
||||
}
|
||||
m_eivec.col(k).normalize();
|
||||
}
|
||||
|
||||
m_eivec = schur.matrixU() * m_eivec;
|
||||
// Step 3: Compute V as V = U X; now A = U T U^* = U X D X^(-1) U^* = V D V^(-1)
|
||||
m_eivec = schur.matrixU() * X;
|
||||
// .. and normalize the eigenvectors
|
||||
for(int k=0 ; k<n ; k++)
|
||||
{
|
||||
m_eivec.col(k).normalize();
|
||||
}
|
||||
m_isInitialized = true;
|
||||
|
||||
// sort the eigenvalues
|
||||
// Step 4: Sort the eigenvalues
|
||||
for (int i=0; i<n; i++)
|
||||
{
|
||||
for (int i=0; i<n; i++)
|
||||
int k;
|
||||
m_eivalues.cwiseAbs().tail(n-i).minCoeff(&k);
|
||||
if (k != 0)
|
||||
{
|
||||
int k;
|
||||
m_eivalues.cwiseAbs().tail(n-i).minCoeff(&k);
|
||||
if (k != 0)
|
||||
{
|
||||
k += i;
|
||||
std::swap(m_eivalues[k],m_eivalues[i]);
|
||||
m_eivec.col(i).swap(m_eivec.col(k));
|
||||
}
|
||||
k += i;
|
||||
std::swap(m_eivalues[k],m_eivalues[i]);
|
||||
m_eivec.col(i).swap(m_eivec.col(k));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,17 +29,28 @@
|
||||
/** \eigenvalues_module \ingroup Eigenvalues_Module
|
||||
* \nonstableyet
|
||||
*
|
||||
* \class ComplexShur
|
||||
* \class ComplexSchur
|
||||
*
|
||||
* \brief Performs a complex Schur decomposition of a real or complex square matrix
|
||||
*
|
||||
* Given a real or complex square matrix A, this class computes the Schur decomposition:
|
||||
* \f$ A = U T U^*\f$ where U is a unitary complex matrix, and T is a complex upper
|
||||
* triangular matrix.
|
||||
* \tparam _MatrixType the type of the matrix of which we are
|
||||
* computing the Schur decomposition; this is expected to be an
|
||||
* instantiation of the Matrix class template.
|
||||
*
|
||||
* The diagonal of the matrix T corresponds to the eigenvalues of the matrix A.
|
||||
* Given a real or complex square matrix A, this class computes the
|
||||
* Schur decomposition: \f$ A = U T U^*\f$ where U is a unitary
|
||||
* complex matrix, and T is a complex upper triangular matrix. The
|
||||
* diagonal of the matrix T corresponds to the eigenvalues of the
|
||||
* matrix A.
|
||||
*
|
||||
* \sa class RealSchur, class EigenSolver
|
||||
* Call the function compute() to compute the Schur decomposition of
|
||||
* a given matrix. Alternatively, you can use the
|
||||
* ComplexSchur(const MatrixType&, bool) constructor which computes
|
||||
* the Schur decomposition at construction time. Once the
|
||||
* decomposition is computed, you can use the matrixU() and matrixT()
|
||||
* functions to retrieve the matrices U and V in the decomposition.
|
||||
*
|
||||
* \sa class RealSchur, class EigenSolver, class ComplexEigenSolver
|
||||
*/
|
||||
template<typename _MatrixType> class ComplexSchur
|
||||
{
|
||||
@@ -52,25 +63,51 @@ template<typename _MatrixType> class ComplexSchur
|
||||
MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime,
|
||||
MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime
|
||||
};
|
||||
typedef typename MatrixType::Scalar Scalar;
|
||||
typedef typename NumTraits<Scalar>::Real RealScalar;
|
||||
typedef std::complex<RealScalar> Complex;
|
||||
typedef Matrix<Complex, RowsAtCompileTime, ColsAtCompileTime, Options, MaxRowsAtCompileTime, MaxColsAtCompileTime> ComplexMatrixType;
|
||||
enum {
|
||||
Size = MatrixType::RowsAtCompileTime
|
||||
};
|
||||
|
||||
/** \brief Default Constructor.
|
||||
/** \brief Scalar type for matrices of type \p _MatrixType. */
|
||||
typedef typename MatrixType::Scalar Scalar;
|
||||
|
||||
typedef typename NumTraits<Scalar>::Real RealScalar;
|
||||
|
||||
/** \brief Complex scalar type for \p _MatrixType.
|
||||
*
|
||||
* The default constructor is useful in cases in which the user intends to
|
||||
* perform decompositions via ComplexSchur::compute().
|
||||
* This is \c std::complex<Scalar> if #Scalar is real (e.g.,
|
||||
* \c float or \c double) and just \c Scalar if #Scalar is
|
||||
* complex.
|
||||
*/
|
||||
ComplexSchur(int size = Size==Dynamic ? 0 : Size)
|
||||
typedef std::complex<RealScalar> ComplexScalar;
|
||||
|
||||
/** \brief Type for the matrices in the Schur decomposition.
|
||||
*
|
||||
* This is a square matrix with entries of type #ComplexScalar.
|
||||
* The size is the same as the size of \p _MatrixType.
|
||||
*/
|
||||
typedef Matrix<ComplexScalar, RowsAtCompileTime, ColsAtCompileTime, Options, MaxRowsAtCompileTime, MaxColsAtCompileTime> ComplexMatrixType;
|
||||
|
||||
/** \brief Default constructor.
|
||||
*
|
||||
* \param [in] size Positive integer, size of the matrix whose Schur decomposition will be computed.
|
||||
*
|
||||
* The default constructor is useful in cases in which the user
|
||||
* intends to perform decompositions via compute(). The \p size
|
||||
* parameter is only used as a hint. It is not an error to give a
|
||||
* wrong \p size, but it may impair performance.
|
||||
*
|
||||
* \sa compute() for an example.
|
||||
*/
|
||||
ComplexSchur(int size = RowsAtCompileTime==Dynamic ? 1 : RowsAtCompileTime)
|
||||
: m_matT(size,size), m_matU(size,size), m_isInitialized(false), m_matUisUptodate(false)
|
||||
{}
|
||||
|
||||
/** Constructor computing the Schur decomposition of the matrix \a matrix.
|
||||
* If \a skipU is true, then the matrix U is not computed. */
|
||||
/** \brief Constructor; computes Schur decomposition of given matrix.
|
||||
*
|
||||
* \param[in] matrix Square matrix whose Schur decomposition is to be computed.
|
||||
* \param[in] skipU If true, then the unitary matrix U in the decomposition is not computed.
|
||||
*
|
||||
* This constructor calls compute() to compute the Schur decomposition.
|
||||
*
|
||||
* \sa matrixT() and matrixU() for examples.
|
||||
*/
|
||||
ComplexSchur(const MatrixType& matrix, bool skipU = false)
|
||||
: m_matT(matrix.rows(),matrix.cols()),
|
||||
m_matU(matrix.rows(),matrix.cols()),
|
||||
@@ -80,7 +117,20 @@ template<typename _MatrixType> class ComplexSchur
|
||||
compute(matrix, skipU);
|
||||
}
|
||||
|
||||
/** \returns a const reference to the matrix U of the respective Schur decomposition. */
|
||||
/** \brief Returns the unitary matrix in the Schur decomposition.
|
||||
*
|
||||
* \returns A const reference to the matrix U.
|
||||
*
|
||||
* It is assumed that either the constructor
|
||||
* ComplexSchur(const MatrixType& matrix, bool skipU) or the
|
||||
* member function compute(const MatrixType& matrix, bool skipU)
|
||||
* has been called before to compute the Schur decomposition of a
|
||||
* matrix, and that \p skipU was set to false (the default
|
||||
* value).
|
||||
*
|
||||
* Example: \include ComplexSchur_matrixU.cpp
|
||||
* Output: \verbinclude ComplexSchur_matrixU.out
|
||||
*/
|
||||
const ComplexMatrixType& matrixU() const
|
||||
{
|
||||
ei_assert(m_isInitialized && "ComplexSchur is not initialized.");
|
||||
@@ -88,24 +138,56 @@ template<typename _MatrixType> class ComplexSchur
|
||||
return m_matU;
|
||||
}
|
||||
|
||||
/** \returns a const reference to the matrix T of the respective Schur decomposition.
|
||||
/** \brief Returns the triangular matrix in the Schur decomposition.
|
||||
*
|
||||
* \returns A const reference to the matrix T.
|
||||
*
|
||||
* It is assumed that either the constructor
|
||||
* ComplexSchur(const MatrixType& matrix, bool skipU) or the
|
||||
* member function compute(const MatrixType& matrix, bool skipU)
|
||||
* has been called before to compute the Schur decomposition of a
|
||||
* matrix.
|
||||
*
|
||||
* Note that this function returns a plain square matrix. If you want to reference
|
||||
* only the upper triangular part, use:
|
||||
* \code schur.matrixT().triangularView<Upper>() \endcode. */
|
||||
* \code schur.matrixT().triangularView<Upper>() \endcode
|
||||
*
|
||||
* Example: \include ComplexSchur_matrixT.cpp
|
||||
* Output: \verbinclude ComplexSchur_matrixT.out
|
||||
*/
|
||||
const ComplexMatrixType& matrixT() const
|
||||
{
|
||||
ei_assert(m_isInitialized && "ComplexShur is not initialized.");
|
||||
ei_assert(m_isInitialized && "ComplexSchur is not initialized.");
|
||||
return m_matT;
|
||||
}
|
||||
|
||||
/** Computes the Schur decomposition of the matrix \a matrix.
|
||||
* If \a skipU is true, then the matrix U is not computed. */
|
||||
/** \brief Computes Schur decomposition of given matrix.
|
||||
*
|
||||
* \param[in] matrix Square matrix whose Schur decomposition is to be computed.
|
||||
* \param[in] skipU If true, then the unitary matrix U in the decomposition is not computed.
|
||||
*
|
||||
* The Schur decomposition is computed by first reducing the
|
||||
* matrix to Hessenberg form using the class
|
||||
* HessenbergDecomposition. The Hessenberg matrix is then reduced
|
||||
* to triangular form by performing QR iterations with a single
|
||||
* shift. The cost of computing the Schur decomposition depends
|
||||
* on the number of iterations; as a rough guide, it may be taken
|
||||
* to be \f$25n^3\f$ complex flops, or \f$10n^3\f$ complex flops
|
||||
* if \a skipU is true.
|
||||
*
|
||||
* Example: \include ComplexSchur_compute.cpp
|
||||
* Output: \verbinclude ComplexSchur_compute.out
|
||||
*/
|
||||
void compute(const MatrixType& matrix, bool skipU = false);
|
||||
|
||||
protected:
|
||||
ComplexMatrixType m_matT, m_matU;
|
||||
bool m_isInitialized;
|
||||
bool m_matUisUptodate;
|
||||
|
||||
private:
|
||||
bool subdiagonalEntryIsNeglegible(int i);
|
||||
ComplexScalar computeShift(int iu, int iter);
|
||||
};
|
||||
|
||||
/** Computes the principal value of the square root of the complex \a z. */
|
||||
@@ -142,9 +224,63 @@ std::complex<RealScalar> ei_sqrt(const std::complex<RealScalar> &z)
|
||||
tim = -tim;
|
||||
|
||||
return (std::complex<RealScalar>(tre,tim));
|
||||
|
||||
}
|
||||
|
||||
|
||||
/** If m_matT(i+1,i) is neglegible in floating point arithmetic
|
||||
* compared to m_matT(i,i) and m_matT(j,j), then set it to zero and
|
||||
* return true, else return false. */
|
||||
template<typename MatrixType>
|
||||
inline bool ComplexSchur<MatrixType>::subdiagonalEntryIsNeglegible(int i)
|
||||
{
|
||||
RealScalar d = ei_norm1(m_matT.coeff(i,i)) + ei_norm1(m_matT.coeff(i+1,i+1));
|
||||
RealScalar sd = ei_norm1(m_matT.coeff(i+1,i));
|
||||
if (ei_isMuchSmallerThan(sd, d, NumTraits<RealScalar>::epsilon()))
|
||||
{
|
||||
m_matT.coeffRef(i+1,i) = ComplexScalar(0);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/** Compute the shift in the current QR iteration. */
|
||||
template<typename MatrixType>
|
||||
typename ComplexSchur<MatrixType>::ComplexScalar ComplexSchur<MatrixType>::computeShift(int iu, int iter)
|
||||
{
|
||||
if (iter == 10 || iter == 20)
|
||||
{
|
||||
// exceptional shift, taken from http://www.netlib.org/eispack/comqr.f
|
||||
return ei_abs(ei_real(m_matT.coeff(iu,iu-1))) + ei_abs(ei_real(m_matT.coeff(iu-1,iu-2)));
|
||||
}
|
||||
|
||||
// compute the shift as one of the eigenvalues of t, the 2x2
|
||||
// diagonal block on the bottom of the active submatrix
|
||||
Matrix<ComplexScalar,2,2> t = m_matT.template block<2,2>(iu-1,iu-1);
|
||||
RealScalar normt = t.cwiseAbs().sum();
|
||||
t /= normt; // the normalization by sf is to avoid under/overflow
|
||||
|
||||
ComplexScalar b = t.coeff(0,1) * t.coeff(1,0);
|
||||
ComplexScalar c = t.coeff(0,0) - t.coeff(1,1);
|
||||
ComplexScalar disc = ei_sqrt(c*c + RealScalar(4)*b);
|
||||
ComplexScalar det = t.coeff(0,0) * t.coeff(1,1) - b;
|
||||
ComplexScalar trace = t.coeff(0,0) + t.coeff(1,1);
|
||||
ComplexScalar eival1 = (trace + disc) / RealScalar(2);
|
||||
ComplexScalar eival2 = (trace - disc) / RealScalar(2);
|
||||
|
||||
if(ei_norm1(eival1) > ei_norm1(eival2))
|
||||
eival2 = det / eival1;
|
||||
else
|
||||
eival1 = det / eival2;
|
||||
|
||||
// choose the eigenvalue closest to the bottom entry of the diagonal
|
||||
if(ei_norm1(eival1-t.coeff(1,1)) < ei_norm1(eival2-t.coeff(1,1)))
|
||||
return normt * eival1;
|
||||
else
|
||||
return normt * eival2;
|
||||
}
|
||||
|
||||
|
||||
template<typename MatrixType>
|
||||
void ComplexSchur<MatrixType>::compute(const MatrixType& matrix, bool skipU)
|
||||
{
|
||||
@@ -156,7 +292,7 @@ void ComplexSchur<MatrixType>::compute(const MatrixType& matrix, bool skipU)
|
||||
if(n==1)
|
||||
{
|
||||
m_matU = ComplexMatrixType::Identity(1,1);
|
||||
if(!skipU) m_matT = matrix;
|
||||
if(!skipU) m_matT = matrix.template cast<ComplexScalar>();
|
||||
m_isInitialized = true;
|
||||
m_matUisUptodate = !skipU;
|
||||
return;
|
||||
@@ -166,9 +302,8 @@ void ComplexSchur<MatrixType>::compute(const MatrixType& matrix, bool skipU)
|
||||
// TODO skip Q if skipU = true
|
||||
HessenbergDecomposition<MatrixType> hess(matrix);
|
||||
|
||||
m_matT = hess.matrixH();
|
||||
if(!skipU) m_matU = hess.matrixQ();
|
||||
|
||||
m_matT = hess.matrixH().template cast<ComplexScalar>();
|
||||
if(!skipU) m_matU = hess.matrixQ().template cast<ComplexScalar>();
|
||||
|
||||
// Reduce the Hessenberg matrix m_matT to triangular form by QR iteration.
|
||||
|
||||
@@ -176,109 +311,62 @@ void ComplexSchur<MatrixType>::compute(const MatrixType& matrix, bool skipU)
|
||||
// Rows 0,...,il-1 are decoupled from the rest because m_matT(il,il-1) is zero.
|
||||
// Rows il,...,iu is the part we are working on (the active submatrix).
|
||||
// Rows iu+1,...,end are already brought in triangular form.
|
||||
|
||||
int iu = m_matT.cols() - 1;
|
||||
int il;
|
||||
RealScalar d,sd,sf;
|
||||
Complex c,b,disc,r1,r2,kappa;
|
||||
int iter = 0; // number of iterations we are working on the (iu,iu) element
|
||||
|
||||
RealScalar eps = NumTraits<RealScalar>::epsilon();
|
||||
|
||||
int iter = 0;
|
||||
while(true)
|
||||
{
|
||||
// find iu, the bottom row of the active submatrix
|
||||
while(iu > 0)
|
||||
{
|
||||
d = ei_norm1(m_matT.coeff(iu,iu)) + ei_norm1(m_matT.coeff(iu-1,iu-1));
|
||||
sd = ei_norm1(m_matT.coeff(iu,iu-1));
|
||||
|
||||
if(!ei_isMuchSmallerThan(sd,d,eps))
|
||||
break;
|
||||
|
||||
m_matT.coeffRef(iu,iu-1) = Complex(0);
|
||||
if(!subdiagonalEntryIsNeglegible(iu-1)) break;
|
||||
iter = 0;
|
||||
--iu;
|
||||
}
|
||||
if(iu==0) break;
|
||||
iter++;
|
||||
|
||||
if(iter >= 30)
|
||||
{
|
||||
// FIXME : what to do when iter==MAXITER ??
|
||||
//std::cerr << "MAXITER" << std::endl;
|
||||
return;
|
||||
}
|
||||
// if iu is zero then we are done; the whole matrix is triangularized
|
||||
if(iu==0) break;
|
||||
|
||||
// if we spent 30 iterations on the current element, we give up
|
||||
iter++;
|
||||
if(iter >= 30) break;
|
||||
|
||||
// find il, the top row of the active submatrix
|
||||
il = iu-1;
|
||||
while(il > 0)
|
||||
while(il > 0 && !subdiagonalEntryIsNeglegible(il-1))
|
||||
{
|
||||
// check if the current 2x2 block on the diagonal is upper triangular
|
||||
d = ei_norm1(m_matT.coeff(il,il)) + ei_norm1(m_matT.coeff(il-1,il-1));
|
||||
sd = ei_norm1(m_matT.coeff(il,il-1));
|
||||
|
||||
if(ei_isMuchSmallerThan(sd,d,eps))
|
||||
break;
|
||||
|
||||
--il;
|
||||
}
|
||||
|
||||
if( il != 0 ) m_matT.coeffRef(il,il-1) = Complex(0);
|
||||
/* perform the QR step using Givens rotations. The first rotation
|
||||
creates a bulge; the (il+2,il) element becomes nonzero. This
|
||||
bulge is chased down to the bottom of the active submatrix. */
|
||||
|
||||
// compute the shift kappa as one of the eigenvalues of the 2x2
|
||||
// diagonal block on the bottom of the active submatrix
|
||||
ComplexScalar shift = computeShift(iu, iter);
|
||||
PlanarRotation<ComplexScalar> rot;
|
||||
rot.makeGivens(m_matT.coeff(il,il) - shift, m_matT.coeff(il+1,il));
|
||||
m_matT.block(0,il,n,n-il).applyOnTheLeft(il, il+1, rot.adjoint());
|
||||
m_matT.block(0,0,std::min(il+2,iu)+1,n).applyOnTheRight(il, il+1, rot);
|
||||
if(!skipU) m_matU.applyOnTheRight(il, il+1, rot);
|
||||
|
||||
Matrix<Scalar,2,2> t = m_matT.template block<2,2>(iu-1,iu-1);
|
||||
sf = t.cwiseAbs().sum();
|
||||
t /= sf; // the normalization by sf is to avoid under/overflow
|
||||
|
||||
b = t.coeff(0,1) * t.coeff(1,0);
|
||||
c = t.coeff(0,0) - t.coeff(1,1);
|
||||
disc = ei_sqrt(c*c + RealScalar(4)*b);
|
||||
|
||||
c = t.coeff(0,0) * t.coeff(1,1) - b;
|
||||
b = t.coeff(0,0) + t.coeff(1,1);
|
||||
r1 = (b+disc)/RealScalar(2);
|
||||
r2 = (b-disc)/RealScalar(2);
|
||||
|
||||
if(ei_norm1(r1) > ei_norm1(r2))
|
||||
r2 = c/r1;
|
||||
else
|
||||
r1 = c/r2;
|
||||
|
||||
if(ei_norm1(r1-t.coeff(1,1)) < ei_norm1(r2-t.coeff(1,1)))
|
||||
kappa = sf * r1;
|
||||
else
|
||||
kappa = sf * r2;
|
||||
|
||||
if (iter == 10 || iter == 20)
|
||||
{
|
||||
// exceptional shift, taken from http://www.netlib.org/eispack/comqr.f
|
||||
kappa = ei_abs(ei_real(m_matT.coeff(iu,iu-1))) + ei_abs(ei_real(m_matT.coeff(iu-1,iu-2)));
|
||||
}
|
||||
|
||||
// perform the QR step using Givens rotations
|
||||
PlanarRotation<Complex> rot;
|
||||
rot.makeGivens(m_matT.coeff(il,il) - kappa, m_matT.coeff(il+1,il));
|
||||
|
||||
for(int i=il ; i<iu ; i++)
|
||||
for(int i=il+1 ; i<iu ; i++)
|
||||
{
|
||||
rot.makeGivens(m_matT.coeffRef(i,i-1), m_matT.coeffRef(i+1,i-1), &m_matT.coeffRef(i,i-1));
|
||||
m_matT.coeffRef(i+1,i-1) = ComplexScalar(0);
|
||||
m_matT.block(0,i,n,n-i).applyOnTheLeft(i, i+1, rot.adjoint());
|
||||
m_matT.block(0,0,std::min(i+2,iu)+1,n).applyOnTheRight(i, i+1, rot);
|
||||
if(!skipU) m_matU.applyOnTheRight(i, i+1, rot);
|
||||
|
||||
if(i != iu-1)
|
||||
{
|
||||
int i1 = i+1;
|
||||
int i2 = i+2;
|
||||
|
||||
rot.makeGivens(m_matT.coeffRef(i1,i), m_matT.coeffRef(i2,i), &m_matT.coeffRef(i1,i));
|
||||
m_matT.coeffRef(i2,i) = Complex(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(iter >= 30)
|
||||
{
|
||||
// FIXME : what to do when iter==MAXITER ??
|
||||
// std::cerr << "MAXITER" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
m_isInitialized = true;
|
||||
m_matUisUptodate = !skipU;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// for linear algebra.
|
||||
//
|
||||
// Copyright (C) 2008 Gael Guennebaud <g.gael@free.fr>
|
||||
// Copyright (C) 2010 Jitse Niesen <jitse@maths.leeds.ac.uk>
|
||||
//
|
||||
// Eigen is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
@@ -25,20 +26,53 @@
|
||||
#ifndef EIGEN_EIGENSOLVER_H
|
||||
#define EIGEN_EIGENSOLVER_H
|
||||
|
||||
#include "./RealSchur.h"
|
||||
|
||||
/** \eigenvalues_module \ingroup Eigenvalues_Module
|
||||
* \nonstableyet
|
||||
*
|
||||
* \class EigenSolver
|
||||
*
|
||||
* \brief Eigen values/vectors solver for non selfadjoint matrices
|
||||
* \brief Computes eigenvalues and eigenvectors of general matrices
|
||||
*
|
||||
* \param MatrixType the type of the matrix of which we are computing the eigen decomposition
|
||||
* \tparam _MatrixType the type of the matrix of which we are computing the
|
||||
* eigendecomposition; this is expected to be an instantiation of the Matrix
|
||||
* class template. Currently, only real matrices are supported.
|
||||
*
|
||||
* Currently it only support real matrices.
|
||||
* The eigenvalues and eigenvectors of a matrix \f$ A \f$ are scalars
|
||||
* \f$ \lambda \f$ and vectors \f$ v \f$ such that \f$ Av = \lambda v \f$. If
|
||||
* \f$ D \f$ is a diagonal matrix with the eigenvalues on the diagonal, and
|
||||
* \f$ V \f$ is a matrix with the eigenvectors as its columns, then \f$ A V =
|
||||
* V D \f$. The matrix \f$ V \f$ is almost always invertible, in which case we
|
||||
* have \f$ A = V D V^{-1} \f$. This is called the eigendecomposition.
|
||||
*
|
||||
* \note this code was adapted from JAMA (public domain)
|
||||
* The eigenvalues and eigenvectors of a matrix may be complex, even when the
|
||||
* matrix is real. However, we can choose real matrices \f$ V \f$ and \f$ D
|
||||
* \f$ satisfying \f$ A V = V D \f$, just like the eigendecomposition, if the
|
||||
* matrix \f$ D \f$ is not required to be diagonal, but if it is allowed to
|
||||
* have blocks of the form
|
||||
* \f[ \begin{bmatrix} u & v \\ -v & u \end{bmatrix} \f]
|
||||
* (where \f$ u \f$ and \f$ v \f$ are real numbers) on the diagonal. These
|
||||
* blocks correspond to complex eigenvalue pairs \f$ u \pm iv \f$. We call
|
||||
* this variant of the eigendecomposition the pseudo-eigendecomposition.
|
||||
*
|
||||
* \sa MatrixBase::eigenvalues(), SelfAdjointEigenSolver
|
||||
* Call the function compute() to compute the eigenvalues and eigenvectors of
|
||||
* a given matrix. Alternatively, you can use the
|
||||
* EigenSolver(const MatrixType&) constructor which computes the eigenvalues
|
||||
* and eigenvectors at construction time. Once the eigenvalue and eigenvectors
|
||||
* are computed, they can be retrieved with the eigenvalues() and
|
||||
* eigenvectors() functions. The pseudoEigenvalueMatrix() and
|
||||
* pseudoEigenvectors() methods allow the construction of the
|
||||
* pseudo-eigendecomposition.
|
||||
*
|
||||
* The documentation for EigenSolver(const MatrixType&) contains an example of
|
||||
* the typical use of this class.
|
||||
*
|
||||
* \note The implementation is adapted from
|
||||
* <a href="http://math.nist.gov/javanumerics/jama/">JAMA</a> (public domain).
|
||||
* Their code is based on EISPACK.
|
||||
*
|
||||
* \sa MatrixBase::eigenvalues(), class ComplexEigenSolver, class SelfAdjointEigenSolver
|
||||
*/
|
||||
template<typename _MatrixType> class EigenSolver
|
||||
{
|
||||
@@ -52,21 +86,54 @@ template<typename _MatrixType> class EigenSolver
|
||||
MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime,
|
||||
MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime
|
||||
};
|
||||
|
||||
/** \brief Scalar type for matrices of type \p _MatrixType. */
|
||||
typedef typename MatrixType::Scalar Scalar;
|
||||
typedef typename NumTraits<Scalar>::Real RealScalar;
|
||||
typedef std::complex<RealScalar> Complex;
|
||||
typedef typename ei_plain_col_type<MatrixType, Complex>::type EigenvalueType;
|
||||
typedef Matrix<Complex, RowsAtCompileTime, ColsAtCompileTime, Options, MaxRowsAtCompileTime, MaxColsAtCompileTime> EigenvectorType;
|
||||
typedef typename ei_plain_col_type<MatrixType, RealScalar>::type RealVectorType;
|
||||
|
||||
/**
|
||||
* \brief Default Constructor.
|
||||
*
|
||||
* The default constructor is useful in cases in which the user intends to
|
||||
* perform decompositions via EigenSolver::compute(const MatrixType&).
|
||||
*/
|
||||
/** \brief Complex scalar type for \p _MatrixType.
|
||||
*
|
||||
* This is \c std::complex<Scalar> if #Scalar is real (e.g.,
|
||||
* \c float or \c double) and just \c Scalar if #Scalar is
|
||||
* complex.
|
||||
*/
|
||||
typedef std::complex<RealScalar> ComplexScalar;
|
||||
|
||||
/** \brief Type for vector of eigenvalues as returned by eigenvalues().
|
||||
*
|
||||
* This is a column vector with entries of type #ComplexScalar.
|
||||
* The length of the vector is the size of \p _MatrixType.
|
||||
*/
|
||||
typedef Matrix<ComplexScalar, ColsAtCompileTime, 1, Options & ~RowMajor, MaxColsAtCompileTime, 1> EigenvalueType;
|
||||
|
||||
/** \brief Type for matrix of eigenvectors as returned by eigenvectors().
|
||||
*
|
||||
* This is a square matrix with entries of type #ComplexScalar.
|
||||
* The size is the same as the size of \p _MatrixType.
|
||||
*/
|
||||
typedef Matrix<ComplexScalar, RowsAtCompileTime, ColsAtCompileTime, Options, MaxRowsAtCompileTime, MaxColsAtCompileTime> EigenvectorsType;
|
||||
|
||||
/** \brief Default constructor.
|
||||
*
|
||||
* The default constructor is useful in cases in which the user intends to
|
||||
* perform decompositions via EigenSolver::compute(const MatrixType&).
|
||||
*
|
||||
* \sa compute() for an example.
|
||||
*/
|
||||
EigenSolver() : m_eivec(), m_eivalues(), m_isInitialized(false) {}
|
||||
|
||||
/** \brief Constructor; computes eigendecomposition of given matrix.
|
||||
*
|
||||
* \param[in] matrix Square matrix whose eigendecomposition is to be computed.
|
||||
*
|
||||
* This constructor calls compute() to compute the eigenvalues
|
||||
* and eigenvectors.
|
||||
*
|
||||
* Example: \include EigenSolver_EigenSolver_MatrixType.cpp
|
||||
* Output: \verbinclude EigenSolver_EigenSolver_MatrixType.out
|
||||
*
|
||||
* \sa compute()
|
||||
*/
|
||||
EigenSolver(const MatrixType& matrix)
|
||||
: m_eivec(matrix.rows(), matrix.cols()),
|
||||
m_eivalues(matrix.cols()),
|
||||
@@ -75,39 +142,42 @@ template<typename _MatrixType> class EigenSolver
|
||||
compute(matrix);
|
||||
}
|
||||
|
||||
/** \brief Returns the eigenvectors of given matrix.
|
||||
*
|
||||
* \returns %Matrix whose columns are the (possibly complex) eigenvectors.
|
||||
*
|
||||
* \pre Either the constructor EigenSolver(const MatrixType&) or the
|
||||
* member function compute(const MatrixType&) has been called before.
|
||||
*
|
||||
* Column \f$ k \f$ of the returned matrix is an eigenvector corresponding
|
||||
* to eigenvalue number \f$ k \f$ as returned by eigenvalues(). The
|
||||
* eigenvectors are normalized to have (Euclidean) norm equal to one. The
|
||||
* matrix returned by this function is the matrix \f$ V \f$ in the
|
||||
* eigendecomposition \f$ A = V D V^{-1} \f$, if it exists.
|
||||
*
|
||||
* Example: \include EigenSolver_eigenvectors.cpp
|
||||
* Output: \verbinclude EigenSolver_eigenvectors.out
|
||||
*
|
||||
* \sa eigenvalues(), pseudoEigenvectors()
|
||||
*/
|
||||
EigenvectorsType eigenvectors() const;
|
||||
|
||||
EigenvectorType eigenvectors(void) const;
|
||||
|
||||
/** \returns a real matrix V of pseudo eigenvectors.
|
||||
/** \brief Returns the pseudo-eigenvectors of given matrix.
|
||||
*
|
||||
* Let D be the block diagonal matrix with the real eigenvalues in 1x1 blocks,
|
||||
* and any complex values u+iv in 2x2 blocks [u v ; -v u]. Then, the matrices D
|
||||
* and V satisfy A*V = V*D.
|
||||
* \returns Const reference to matrix whose columns are the pseudo-eigenvectors.
|
||||
*
|
||||
* More precisely, if the diagonal matrix of the eigen values is:\n
|
||||
* \f$
|
||||
* \left[ \begin{array}{cccccc}
|
||||
* u+iv & & & & & \\
|
||||
* & u-iv & & & & \\
|
||||
* & & a+ib & & & \\
|
||||
* & & & a-ib & & \\
|
||||
* & & & & x & \\
|
||||
* & & & & & y \\
|
||||
* \end{array} \right]
|
||||
* \f$ \n
|
||||
* then, we have:\n
|
||||
* \f$
|
||||
* D =\left[ \begin{array}{cccccc}
|
||||
* u & v & & & & \\
|
||||
* -v & u & & & & \\
|
||||
* & & a & b & & \\
|
||||
* & & -b & a & & \\
|
||||
* & & & & x & \\
|
||||
* & & & & & y \\
|
||||
* \end{array} \right]
|
||||
* \f$
|
||||
* \pre Either the constructor EigenSolver(const MatrixType&) or
|
||||
* the member function compute(const MatrixType&) has been called
|
||||
* before.
|
||||
*
|
||||
* \sa pseudoEigenvalueMatrix()
|
||||
* The real matrix \f$ V \f$ returned by this function and the
|
||||
* block-diagonal matrix \f$ D \f$ returned by pseudoEigenvalueMatrix()
|
||||
* satisfy \f$ AV = VD \f$.
|
||||
*
|
||||
* Example: \include EigenSolver_pseudoEigenvectors.cpp
|
||||
* Output: \verbinclude EigenSolver_pseudoEigenvectors.out
|
||||
*
|
||||
* \sa pseudoEigenvalueMatrix(), eigenvectors()
|
||||
*/
|
||||
const MatrixType& pseudoEigenvectors() const
|
||||
{
|
||||
@@ -115,21 +185,72 @@ template<typename _MatrixType> class EigenSolver
|
||||
return m_eivec;
|
||||
}
|
||||
|
||||
/** \brief Returns the block-diagonal matrix in the pseudo-eigendecomposition.
|
||||
*
|
||||
* \returns A block-diagonal matrix.
|
||||
*
|
||||
* \pre Either the constructor EigenSolver(const MatrixType&) or the
|
||||
* member function compute(const MatrixType&) has been called before.
|
||||
*
|
||||
* The matrix \f$ D \f$ returned by this function is real and
|
||||
* block-diagonal. The blocks on the diagonal are either 1-by-1 or 2-by-2
|
||||
* blocks of the form
|
||||
* \f$ \begin{bmatrix} u & v \\ -v & u \end{bmatrix} \f$.
|
||||
* The matrix \f$ D \f$ and the matrix \f$ V \f$ returned by
|
||||
* pseudoEigenvectors() satisfy \f$ AV = VD \f$.
|
||||
*
|
||||
* \sa pseudoEigenvectors() for an example, eigenvalues()
|
||||
*/
|
||||
MatrixType pseudoEigenvalueMatrix() const;
|
||||
|
||||
/** \returns the eigenvalues as a column vector */
|
||||
/** \brief Returns the eigenvalues of given matrix.
|
||||
*
|
||||
* \returns Column vector containing the eigenvalues.
|
||||
*
|
||||
* \pre Either the constructor EigenSolver(const MatrixType&) or the
|
||||
* member function compute(const MatrixType&) has been called before.
|
||||
*
|
||||
* The eigenvalues are repeated according to their algebraic multiplicity,
|
||||
* so there are as many eigenvalues as rows in the matrix.
|
||||
*
|
||||
* Example: \include EigenSolver_eigenvalues.cpp
|
||||
* Output: \verbinclude EigenSolver_eigenvalues.out
|
||||
*
|
||||
* \sa eigenvectors(), pseudoEigenvalueMatrix(),
|
||||
* MatrixBase::eigenvalues()
|
||||
*/
|
||||
EigenvalueType eigenvalues() const
|
||||
{
|
||||
ei_assert(m_isInitialized && "EigenSolver is not initialized.");
|
||||
return m_eivalues;
|
||||
}
|
||||
|
||||
/** \brief Computes eigendecomposition of given matrix.
|
||||
*
|
||||
* \param[in] matrix Square matrix whose eigendecomposition is to be computed.
|
||||
* \returns Reference to \c *this
|
||||
*
|
||||
* This function computes the eigenvalues and eigenvectors of \p matrix.
|
||||
* The eigenvalues() and eigenvectors() functions can be used to retrieve
|
||||
* the computed eigendecomposition.
|
||||
*
|
||||
* The matrix is first reduced to real Schur form using the RealSchur
|
||||
* class. The Schur decomposition is then used to compute the eigenvalues
|
||||
* and eigenvectors.
|
||||
*
|
||||
* The cost of the computation is dominated by the cost of the Schur
|
||||
* decomposition, which is very approximately \f$ 25n^3 \f$ where
|
||||
* \f$ n \f$ is the size of the matrix.
|
||||
*
|
||||
* This method reuses of the allocated data in the EigenSolver object.
|
||||
*
|
||||
* Example: \include EigenSolver_compute.cpp
|
||||
* Output: \verbinclude EigenSolver_compute.out
|
||||
*/
|
||||
EigenSolver& compute(const MatrixType& matrix);
|
||||
|
||||
private:
|
||||
|
||||
void orthes(MatrixType& matH, RealVectorType& ort);
|
||||
void hqr2(MatrixType& matH);
|
||||
void hqr2_step2(MatrixType& matH);
|
||||
|
||||
protected:
|
||||
MatrixType m_eivec;
|
||||
@@ -137,10 +258,6 @@ template<typename _MatrixType> class EigenSolver
|
||||
bool m_isInitialized;
|
||||
};
|
||||
|
||||
/** \returns the real block diagonal matrix D of the eigenvalues.
|
||||
*
|
||||
* See pseudoEigenvectors() for the details.
|
||||
*/
|
||||
template<typename MatrixType>
|
||||
MatrixType EigenSolver<MatrixType>::pseudoEigenvalueMatrix() const
|
||||
{
|
||||
@@ -161,30 +278,26 @@ MatrixType EigenSolver<MatrixType>::pseudoEigenvalueMatrix() const
|
||||
return matD;
|
||||
}
|
||||
|
||||
/** \returns the normalized complex eigenvectors as a matrix of column vectors.
|
||||
*
|
||||
* \sa eigenvalues(), pseudoEigenvectors()
|
||||
*/
|
||||
template<typename MatrixType>
|
||||
typename EigenSolver<MatrixType>::EigenvectorType EigenSolver<MatrixType>::eigenvectors(void) const
|
||||
typename EigenSolver<MatrixType>::EigenvectorsType EigenSolver<MatrixType>::eigenvectors() const
|
||||
{
|
||||
ei_assert(m_isInitialized && "EigenSolver is not initialized.");
|
||||
int n = m_eivec.cols();
|
||||
EigenvectorType matV(n,n);
|
||||
EigenvectorsType matV(n,n);
|
||||
for (int j=0; j<n; ++j)
|
||||
{
|
||||
if (ei_isMuchSmallerThan(ei_abs(ei_imag(m_eivalues.coeff(j))), ei_abs(ei_real(m_eivalues.coeff(j)))))
|
||||
{
|
||||
// we have a real eigen value
|
||||
matV.col(j) = m_eivec.col(j).template cast<Complex>();
|
||||
matV.col(j) = m_eivec.col(j).template cast<ComplexScalar>();
|
||||
}
|
||||
else
|
||||
{
|
||||
// we have a pair of complex eigen values
|
||||
for (int i=0; i<n; ++i)
|
||||
{
|
||||
matV.coeffRef(i,j) = Complex(m_eivec.coeff(i,j), m_eivec.coeff(i,j+1));
|
||||
matV.coeffRef(i,j+1) = Complex(m_eivec.coeff(i,j), -m_eivec.coeff(i,j+1));
|
||||
matV.coeffRef(i,j) = ComplexScalar(m_eivec.coeff(i,j), m_eivec.coeff(i,j+1));
|
||||
matV.coeffRef(i,j+1) = ComplexScalar(m_eivec.coeff(i,j), -m_eivec.coeff(i,j+1));
|
||||
}
|
||||
matV.col(j).normalize();
|
||||
matV.col(j+1).normalize();
|
||||
@@ -198,86 +311,39 @@ template<typename MatrixType>
|
||||
EigenSolver<MatrixType>& EigenSolver<MatrixType>::compute(const MatrixType& matrix)
|
||||
{
|
||||
assert(matrix.cols() == matrix.rows());
|
||||
int n = matrix.cols();
|
||||
m_eivalues.resize(n,1);
|
||||
m_eivec.resize(n,n);
|
||||
|
||||
MatrixType matH = matrix;
|
||||
RealVectorType ort(n);
|
||||
// Reduce to real Schur form.
|
||||
RealSchur<MatrixType> rs(matrix);
|
||||
MatrixType matT = rs.matrixT();
|
||||
m_eivec = rs.matrixU();
|
||||
|
||||
// Reduce to Hessenberg form.
|
||||
orthes(matH, ort);
|
||||
|
||||
// Reduce Hessenberg to real Schur form.
|
||||
hqr2(matH);
|
||||
// Compute eigenvalues from matT
|
||||
m_eivalues.resize(matrix.cols());
|
||||
int i = 0;
|
||||
while (i < matrix.cols())
|
||||
{
|
||||
if (i == matrix.cols() - 1 || matT.coeff(i+1, i) == Scalar(0))
|
||||
{
|
||||
m_eivalues.coeffRef(i) = matT.coeff(i, i);
|
||||
++i;
|
||||
}
|
||||
else
|
||||
{
|
||||
Scalar p = Scalar(0.5) * (matT.coeff(i, i) - matT.coeff(i+1, i+1));
|
||||
Scalar z = ei_sqrt(ei_abs(p * p + matT.coeff(i+1, i) * matT.coeff(i, i+1)));
|
||||
m_eivalues.coeffRef(i) = ComplexScalar(matT.coeff(i+1, i+1) + p, z);
|
||||
m_eivalues.coeffRef(i+1) = ComplexScalar(matT.coeff(i+1, i+1) + p, -z);
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
|
||||
// Compute eigenvectors.
|
||||
hqr2_step2(matT);
|
||||
|
||||
m_isInitialized = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Nonsymmetric reduction to Hessenberg form.
|
||||
template<typename MatrixType>
|
||||
void EigenSolver<MatrixType>::orthes(MatrixType& matH, RealVectorType& ort)
|
||||
{
|
||||
// This is derived from the Algol procedures orthes and ortran,
|
||||
// by Martin and Wilkinson, Handbook for Auto. Comp.,
|
||||
// Vol.ii-Linear Algebra, and the corresponding
|
||||
// Fortran subroutines in EISPACK.
|
||||
|
||||
int n = m_eivec.cols();
|
||||
int low = 0;
|
||||
int high = n-1;
|
||||
|
||||
for (int m = low+1; m <= high-1; ++m)
|
||||
{
|
||||
// Scale column.
|
||||
RealScalar scale = matH.block(m, m-1, high-m+1, 1).cwiseAbs().sum();
|
||||
if (scale != 0.0)
|
||||
{
|
||||
// Compute Householder transformation.
|
||||
RealScalar h = 0.0;
|
||||
// FIXME could be rewritten, but this one looks better wrt cache
|
||||
for (int i = high; i >= m; i--)
|
||||
{
|
||||
ort.coeffRef(i) = matH.coeff(i,m-1)/scale;
|
||||
h += ort.coeff(i) * ort.coeff(i);
|
||||
}
|
||||
RealScalar g = ei_sqrt(h);
|
||||
if (ort.coeff(m) > 0)
|
||||
g = -g;
|
||||
h = h - ort.coeff(m) * g;
|
||||
ort.coeffRef(m) = ort.coeff(m) - g;
|
||||
|
||||
// Apply Householder similarity transformation
|
||||
// H = (I-u*u'/h)*H*(I-u*u')/h)
|
||||
int bSize = high-m+1;
|
||||
matH.block(m, m, bSize, n-m).noalias() -= ((ort.segment(m, bSize)/h)
|
||||
* (ort.segment(m, bSize).transpose() * matH.block(m, m, bSize, n-m)));
|
||||
|
||||
matH.block(0, m, high+1, bSize).noalias() -= ((matH.block(0, m, high+1, bSize) * ort.segment(m, bSize))
|
||||
* (ort.segment(m, bSize)/h).transpose());
|
||||
|
||||
ort.coeffRef(m) = scale*ort.coeff(m);
|
||||
matH.coeffRef(m,m-1) = scale*g;
|
||||
}
|
||||
}
|
||||
|
||||
// Accumulate transformations (Algol's ortran).
|
||||
m_eivec.setIdentity();
|
||||
|
||||
for (int m = high-1; m >= low+1; m--)
|
||||
{
|
||||
if (matH.coeff(m,m-1) != 0.0)
|
||||
{
|
||||
ort.segment(m+1, high-m) = matH.col(m-1).segment(m+1, high-m);
|
||||
|
||||
int bSize = high-m+1;
|
||||
m_eivec.block(m, m, bSize, bSize).noalias() += ( (ort.segment(m, bSize) / (matH.coeff(m,m-1) * ort.coeff(m)))
|
||||
* (ort.segment(m, bSize).transpose() * m_eivec.block(m, m, bSize, bSize)) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Complex scalar division.
|
||||
template<typename Scalar>
|
||||
std::complex<Scalar> cdiv(Scalar xr, Scalar xi, Scalar yr, Scalar yi)
|
||||
@@ -298,289 +364,29 @@ std::complex<Scalar> cdiv(Scalar xr, Scalar xi, Scalar yr, Scalar yi)
|
||||
}
|
||||
|
||||
|
||||
// Nonsymmetric reduction from Hessenberg to real Schur form.
|
||||
template<typename MatrixType>
|
||||
void EigenSolver<MatrixType>::hqr2(MatrixType& matH)
|
||||
void EigenSolver<MatrixType>::hqr2_step2(MatrixType& matH)
|
||||
{
|
||||
// This is derived from the Algol procedure hqr2,
|
||||
// by Martin and Wilkinson, Handbook for Auto. Comp.,
|
||||
// Vol.ii-Linear Algebra, and the corresponding
|
||||
// Fortran subroutine in EISPACK.
|
||||
const int nn = m_eivec.cols();
|
||||
const int low = 0;
|
||||
const int high = nn-1;
|
||||
const Scalar eps = ei_pow(Scalar(2),ei_is_same_type<Scalar,float>::ret ? Scalar(-23) : Scalar(-52));
|
||||
Scalar p, q, r=0, s=0, t, w, x, y, z=0;
|
||||
|
||||
// Initialize
|
||||
int nn = m_eivec.cols();
|
||||
int n = nn-1;
|
||||
int low = 0;
|
||||
int high = nn-1;
|
||||
Scalar eps = ei_pow(Scalar(2),ei_is_same_type<Scalar,float>::ret ? Scalar(-23) : Scalar(-52));
|
||||
Scalar exshift = 0.0;
|
||||
Scalar p=0,q=0,r=0,s=0,z=0,t,w,x,y;
|
||||
|
||||
// Store roots isolated by balanc and compute matrix norm
|
||||
// FIXME to be efficient the following would requires a triangular reduxion code
|
||||
// Scalar norm = matH.upper().cwiseAbs().sum() + matH.corner(BottomLeft,n,n).diagonal().cwiseAbs().sum();
|
||||
// inefficient! this is already computed in RealSchur
|
||||
Scalar norm = 0.0;
|
||||
for (int j = 0; j < nn; ++j)
|
||||
{
|
||||
// FIXME what's the purpose of the following since the condition is always false
|
||||
if ((j < low) || (j > high))
|
||||
{
|
||||
m_eivalues.coeffRef(j) = Complex(matH.coeff(j,j), 0.0);
|
||||
}
|
||||
norm += matH.row(j).segment(std::max(j-1,0), nn-std::max(j-1,0)).cwiseAbs().sum();
|
||||
}
|
||||
|
||||
// Outer loop over eigenvalue index
|
||||
int iter = 0;
|
||||
while (n >= low)
|
||||
{
|
||||
// Look for single small sub-diagonal element
|
||||
int l = n;
|
||||
while (l > low)
|
||||
{
|
||||
s = ei_abs(matH.coeff(l-1,l-1)) + ei_abs(matH.coeff(l,l));
|
||||
if (s == 0.0)
|
||||
s = norm;
|
||||
if (ei_abs(matH.coeff(l,l-1)) < eps * s)
|
||||
break;
|
||||
l--;
|
||||
}
|
||||
|
||||
// Check for convergence
|
||||
// One root found
|
||||
if (l == n)
|
||||
{
|
||||
matH.coeffRef(n,n) = matH.coeff(n,n) + exshift;
|
||||
m_eivalues.coeffRef(n) = Complex(matH.coeff(n,n), 0.0);
|
||||
n--;
|
||||
iter = 0;
|
||||
}
|
||||
else if (l == n-1) // Two roots found
|
||||
{
|
||||
w = matH.coeff(n,n-1) * matH.coeff(n-1,n);
|
||||
p = (matH.coeff(n-1,n-1) - matH.coeff(n,n)) * Scalar(0.5);
|
||||
q = p * p + w;
|
||||
z = ei_sqrt(ei_abs(q));
|
||||
matH.coeffRef(n,n) = matH.coeff(n,n) + exshift;
|
||||
matH.coeffRef(n-1,n-1) = matH.coeff(n-1,n-1) + exshift;
|
||||
x = matH.coeff(n,n);
|
||||
|
||||
// Scalar pair
|
||||
if (q >= 0)
|
||||
{
|
||||
if (p >= 0)
|
||||
z = p + z;
|
||||
else
|
||||
z = p - z;
|
||||
|
||||
m_eivalues.coeffRef(n-1) = Complex(x + z, 0.0);
|
||||
m_eivalues.coeffRef(n) = Complex(z!=0.0 ? x - w / z : m_eivalues.coeff(n-1).real(), 0.0);
|
||||
|
||||
x = matH.coeff(n,n-1);
|
||||
s = ei_abs(x) + ei_abs(z);
|
||||
p = x / s;
|
||||
q = z / s;
|
||||
r = ei_sqrt(p * p+q * q);
|
||||
p = p / r;
|
||||
q = q / r;
|
||||
|
||||
// Row modification
|
||||
for (int j = n-1; j < nn; ++j)
|
||||
{
|
||||
z = matH.coeff(n-1,j);
|
||||
matH.coeffRef(n-1,j) = q * z + p * matH.coeff(n,j);
|
||||
matH.coeffRef(n,j) = q * matH.coeff(n,j) - p * z;
|
||||
}
|
||||
|
||||
// Column modification
|
||||
for (int i = 0; i <= n; ++i)
|
||||
{
|
||||
z = matH.coeff(i,n-1);
|
||||
matH.coeffRef(i,n-1) = q * z + p * matH.coeff(i,n);
|
||||
matH.coeffRef(i,n) = q * matH.coeff(i,n) - p * z;
|
||||
}
|
||||
|
||||
// Accumulate transformations
|
||||
for (int i = low; i <= high; ++i)
|
||||
{
|
||||
z = m_eivec.coeff(i,n-1);
|
||||
m_eivec.coeffRef(i,n-1) = q * z + p * m_eivec.coeff(i,n);
|
||||
m_eivec.coeffRef(i,n) = q * m_eivec.coeff(i,n) - p * z;
|
||||
}
|
||||
}
|
||||
else // Complex pair
|
||||
{
|
||||
m_eivalues.coeffRef(n-1) = Complex(x + p, z);
|
||||
m_eivalues.coeffRef(n) = Complex(x + p, -z);
|
||||
}
|
||||
n = n - 2;
|
||||
iter = 0;
|
||||
}
|
||||
else // No convergence yet
|
||||
{
|
||||
// Form shift
|
||||
x = matH.coeff(n,n);
|
||||
y = 0.0;
|
||||
w = 0.0;
|
||||
if (l < n)
|
||||
{
|
||||
y = matH.coeff(n-1,n-1);
|
||||
w = matH.coeff(n,n-1) * matH.coeff(n-1,n);
|
||||
}
|
||||
|
||||
// Wilkinson's original ad hoc shift
|
||||
if (iter == 10)
|
||||
{
|
||||
exshift += x;
|
||||
for (int i = low; i <= n; ++i)
|
||||
matH.coeffRef(i,i) -= x;
|
||||
s = ei_abs(matH.coeff(n,n-1)) + ei_abs(matH.coeff(n-1,n-2));
|
||||
x = y = Scalar(0.75) * s;
|
||||
w = Scalar(-0.4375) * s * s;
|
||||
}
|
||||
|
||||
// MATLAB's new ad hoc shift
|
||||
if (iter == 30)
|
||||
{
|
||||
s = Scalar((y - x) / 2.0);
|
||||
s = s * s + w;
|
||||
if (s > 0)
|
||||
{
|
||||
s = ei_sqrt(s);
|
||||
if (y < x)
|
||||
s = -s;
|
||||
s = Scalar(x - w / ((y - x) / 2.0 + s));
|
||||
for (int i = low; i <= n; ++i)
|
||||
matH.coeffRef(i,i) -= s;
|
||||
exshift += s;
|
||||
x = y = w = Scalar(0.964);
|
||||
}
|
||||
}
|
||||
|
||||
iter = iter + 1; // (Could check iteration count here.)
|
||||
|
||||
// Look for two consecutive small sub-diagonal elements
|
||||
int m = n-2;
|
||||
while (m >= l)
|
||||
{
|
||||
z = matH.coeff(m,m);
|
||||
r = x - z;
|
||||
s = y - z;
|
||||
p = (r * s - w) / matH.coeff(m+1,m) + matH.coeff(m,m+1);
|
||||
q = matH.coeff(m+1,m+1) - z - r - s;
|
||||
r = matH.coeff(m+2,m+1);
|
||||
s = ei_abs(p) + ei_abs(q) + ei_abs(r);
|
||||
p = p / s;
|
||||
q = q / s;
|
||||
r = r / s;
|
||||
if (m == l) {
|
||||
break;
|
||||
}
|
||||
if (ei_abs(matH.coeff(m,m-1)) * (ei_abs(q) + ei_abs(r)) <
|
||||
eps * (ei_abs(p) * (ei_abs(matH.coeff(m-1,m-1)) + ei_abs(z) +
|
||||
ei_abs(matH.coeff(m+1,m+1)))))
|
||||
{
|
||||
break;
|
||||
}
|
||||
m--;
|
||||
}
|
||||
|
||||
for (int i = m+2; i <= n; ++i)
|
||||
{
|
||||
matH.coeffRef(i,i-2) = 0.0;
|
||||
if (i > m+2)
|
||||
matH.coeffRef(i,i-3) = 0.0;
|
||||
}
|
||||
|
||||
// Double QR step involving rows l:n and columns m:n
|
||||
for (int k = m; k <= n-1; ++k)
|
||||
{
|
||||
int notlast = (k != n-1);
|
||||
if (k != m) {
|
||||
p = matH.coeff(k,k-1);
|
||||
q = matH.coeff(k+1,k-1);
|
||||
r = notlast ? matH.coeff(k+2,k-1) : Scalar(0);
|
||||
x = ei_abs(p) + ei_abs(q) + ei_abs(r);
|
||||
if (x != 0.0)
|
||||
{
|
||||
p = p / x;
|
||||
q = q / x;
|
||||
r = r / x;
|
||||
}
|
||||
}
|
||||
|
||||
if (x == 0.0)
|
||||
break;
|
||||
|
||||
s = ei_sqrt(p * p + q * q + r * r);
|
||||
|
||||
if (p < 0)
|
||||
s = -s;
|
||||
|
||||
if (s != 0)
|
||||
{
|
||||
if (k != m)
|
||||
matH.coeffRef(k,k-1) = -s * x;
|
||||
else if (l != m)
|
||||
matH.coeffRef(k,k-1) = -matH.coeff(k,k-1);
|
||||
|
||||
p = p + s;
|
||||
x = p / s;
|
||||
y = q / s;
|
||||
z = r / s;
|
||||
q = q / p;
|
||||
r = r / p;
|
||||
|
||||
// Row modification
|
||||
for (int j = k; j < nn; ++j)
|
||||
{
|
||||
p = matH.coeff(k,j) + q * matH.coeff(k+1,j);
|
||||
if (notlast)
|
||||
{
|
||||
p = p + r * matH.coeff(k+2,j);
|
||||
matH.coeffRef(k+2,j) = matH.coeff(k+2,j) - p * z;
|
||||
}
|
||||
matH.coeffRef(k,j) = matH.coeff(k,j) - p * x;
|
||||
matH.coeffRef(k+1,j) = matH.coeff(k+1,j) - p * y;
|
||||
}
|
||||
|
||||
// Column modification
|
||||
for (int i = 0; i <= std::min(n,k+3); ++i)
|
||||
{
|
||||
p = x * matH.coeff(i,k) + y * matH.coeff(i,k+1);
|
||||
if (notlast)
|
||||
{
|
||||
p = p + z * matH.coeff(i,k+2);
|
||||
matH.coeffRef(i,k+2) = matH.coeff(i,k+2) - p * r;
|
||||
}
|
||||
matH.coeffRef(i,k) = matH.coeff(i,k) - p;
|
||||
matH.coeffRef(i,k+1) = matH.coeff(i,k+1) - p * q;
|
||||
}
|
||||
|
||||
// Accumulate transformations
|
||||
for (int i = low; i <= high; ++i)
|
||||
{
|
||||
p = x * m_eivec.coeff(i,k) + y * m_eivec.coeff(i,k+1);
|
||||
if (notlast)
|
||||
{
|
||||
p = p + z * m_eivec.coeff(i,k+2);
|
||||
m_eivec.coeffRef(i,k+2) = m_eivec.coeff(i,k+2) - p * r;
|
||||
}
|
||||
m_eivec.coeffRef(i,k) = m_eivec.coeff(i,k) - p;
|
||||
m_eivec.coeffRef(i,k+1) = m_eivec.coeff(i,k+1) - p * q;
|
||||
}
|
||||
} // (s != 0)
|
||||
} // k loop
|
||||
} // check convergence
|
||||
} // while (n >= low)
|
||||
|
||||
|
||||
// Backsubstitute to find vectors of upper triangular form
|
||||
if (norm == 0.0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (n = nn-1; n >= 0; n--)
|
||||
for (int n = nn-1; n >= 0; n--)
|
||||
{
|
||||
p = m_eivalues.coeff(n).real();
|
||||
q = m_eivalues.coeff(n).imag();
|
||||
|
||||
@@ -30,14 +30,30 @@
|
||||
*
|
||||
* \class HessenbergDecomposition
|
||||
*
|
||||
* \brief Reduces a squared matrix to an Hessemberg form
|
||||
* \brief Reduces a square matrix to Hessenberg form by an orthogonal similarity transformation
|
||||
*
|
||||
* \param MatrixType the type of the matrix of which we are computing the Hessenberg decomposition
|
||||
* \tparam _MatrixType the type of the matrix of which we are computing the Hessenberg decomposition
|
||||
*
|
||||
* This class performs an Hessenberg decomposition of a matrix \f$ A \f$ such that:
|
||||
* \f$ A = Q H Q^* \f$ where \f$ Q \f$ is unitary and \f$ H \f$ a Hessenberg matrix.
|
||||
* This class performs an Hessenberg decomposition of a matrix \f$ A \f$. In
|
||||
* the real case, the Hessenberg decomposition consists of an orthogonal
|
||||
* matrix \f$ Q \f$ and a Hessenberg matrix \f$ H \f$ such that \f$ A = Q H
|
||||
* Q^T \f$. An orthogonal matrix is a matrix whose inverse equals its
|
||||
* transpose (\f$ Q^{-1} = Q^T \f$). A Hessenberg matrix has zeros below the
|
||||
* subdiagonal, so it is almost upper triangular. The Hessenberg decomposition
|
||||
* of a complex matrix is \f$ A = Q H Q^* \f$ with \f$ Q \f$ unitary (that is,
|
||||
* \f$ Q^{-1} = Q^* \f$).
|
||||
*
|
||||
* \sa class Tridiagonalization, class Qr
|
||||
* Call the function compute() to compute the Hessenberg decomposition of a
|
||||
* given matrix. Alternatively, you can use the
|
||||
* HessenbergDecomposition(const MatrixType&) constructor which computes the
|
||||
* Hessenberg decomposition at construction time. Once the decomposition is
|
||||
* computed, you can use the matrixH() and matrixQ() functions to construct
|
||||
* the matrices H and Q in the decomposition.
|
||||
*
|
||||
* The documentation for matrixH() contains an example of the typical use of
|
||||
* this class.
|
||||
*
|
||||
* \sa class ComplexSchur, class Tridiagonalization, \ref QR_Module "QR Module"
|
||||
*/
|
||||
template<typename _MatrixType> class HessenbergDecomposition
|
||||
{
|
||||
@@ -51,13 +67,28 @@ template<typename _MatrixType> class HessenbergDecomposition
|
||||
MaxSize = MatrixType::MaxRowsAtCompileTime,
|
||||
MaxSizeMinusOne = MaxSize == Dynamic ? Dynamic : MaxSize - 1
|
||||
};
|
||||
typedef typename MatrixType::Scalar Scalar;
|
||||
typedef typename NumTraits<Scalar>::Real RealScalar;
|
||||
typedef Matrix<Scalar, SizeMinusOne, 1, Options & ~RowMajor, MaxSizeMinusOne, 1> CoeffVectorType;
|
||||
typedef typename ei_plain_col_type<MatrixType>::type VectorType;
|
||||
|
||||
/** This constructor initializes a HessenbergDecomposition object for
|
||||
* further use with HessenbergDecomposition::compute()
|
||||
/** \brief Scalar type for matrices of type \p _MatrixType. */
|
||||
typedef typename MatrixType::Scalar Scalar;
|
||||
|
||||
/** \brief Type for vector of Householder coefficients.
|
||||
*
|
||||
* This is column vector with entries of type #Scalar. The length of the
|
||||
* vector is one less than the size of \p _MatrixType, if it is a
|
||||
* fixed-side type.
|
||||
*/
|
||||
typedef Matrix<Scalar, SizeMinusOne, 1, Options & ~RowMajor, MaxSizeMinusOne, 1> CoeffVectorType;
|
||||
|
||||
/** \brief Default constructor; the decomposition will be computed later.
|
||||
*
|
||||
* \param [in] size The size of the matrix whose Hessenberg decomposition will be computed.
|
||||
*
|
||||
* The default constructor is useful in cases in which the user intends to
|
||||
* perform decompositions via compute(). The \p size parameter is only
|
||||
* used as a hint. It is not an error to give a wrong \p size, but it may
|
||||
* impair performance.
|
||||
*
|
||||
* \sa compute() for an example.
|
||||
*/
|
||||
HessenbergDecomposition(int size = Size==Dynamic ? 2 : Size)
|
||||
: m_matrix(size,size)
|
||||
@@ -66,6 +97,15 @@ template<typename _MatrixType> class HessenbergDecomposition
|
||||
m_hCoeffs.resize(size-1);
|
||||
}
|
||||
|
||||
/** \brief Constructor; computes Hessenberg decomposition of given matrix.
|
||||
*
|
||||
* \param[in] matrix Square matrix whose Hessenberg decomposition is to be computed.
|
||||
*
|
||||
* This constructor calls compute() to compute the Hessenberg
|
||||
* decomposition.
|
||||
*
|
||||
* \sa matrixH() for an example.
|
||||
*/
|
||||
HessenbergDecomposition(const MatrixType& matrix)
|
||||
: m_matrix(matrix)
|
||||
{
|
||||
@@ -75,9 +115,21 @@ template<typename _MatrixType> class HessenbergDecomposition
|
||||
_compute(m_matrix, m_hCoeffs);
|
||||
}
|
||||
|
||||
/** Computes or re-compute the Hessenberg decomposition for the matrix \a matrix.
|
||||
/** \brief Computes Hessenberg decomposition of given matrix.
|
||||
*
|
||||
* \param[in] matrix Square matrix whose Hessenberg decomposition is to be computed.
|
||||
*
|
||||
* This method allows to re-use the allocated data.
|
||||
* The Hessenberg decomposition is computed by bringing the columns of the
|
||||
* matrix successively in the required form using Householder reflections
|
||||
* (see, e.g., Algorithm 7.4.2 in Golub \& Van Loan, <i>%Matrix
|
||||
* Computations</i>). The cost is \f$ 10n^3/3 \f$ flops, where \f$ n \f$
|
||||
* denotes the size of the given matrix.
|
||||
*
|
||||
* This method reuses of the allocated data in the HessenbergDecomposition
|
||||
* object.
|
||||
*
|
||||
* Example: \include HessenbergDecomposition_compute.cpp
|
||||
* Output: \verbinclude HessenbergDecomposition_compute.out
|
||||
*/
|
||||
void compute(const MatrixType& matrix)
|
||||
{
|
||||
@@ -88,36 +140,95 @@ template<typename _MatrixType> class HessenbergDecomposition
|
||||
_compute(m_matrix, m_hCoeffs);
|
||||
}
|
||||
|
||||
/** \returns a const reference to the householder coefficients allowing to
|
||||
* reconstruct the matrix Q from the packed data.
|
||||
/** \brief Returns the Householder coefficients.
|
||||
*
|
||||
* \sa packedMatrix()
|
||||
* \returns a const reference to the vector of Householder coefficients
|
||||
*
|
||||
* \pre Either the constructor HessenbergDecomposition(const MatrixType&)
|
||||
* or the member function compute(const MatrixType&) has been called
|
||||
* before to compute the Hessenberg decomposition of a matrix.
|
||||
*
|
||||
* The Householder coefficients allow the reconstruction of the matrix
|
||||
* \f$ Q \f$ in the Hessenberg decomposition from the packed data.
|
||||
*
|
||||
* \sa packedMatrix(), \ref Householder_Module "Householder module"
|
||||
*/
|
||||
const CoeffVectorType& householderCoefficients() const { return m_hCoeffs; }
|
||||
|
||||
/** \returns a const reference to the internal representation of the decomposition.
|
||||
/** \brief Returns the internal representation of the decomposition
|
||||
*
|
||||
* \returns a const reference to a matrix with the internal representation
|
||||
* of the decomposition.
|
||||
*
|
||||
* \pre Either the constructor HessenbergDecomposition(const MatrixType&)
|
||||
* or the member function compute(const MatrixType&) has been called
|
||||
* before to compute the Hessenberg decomposition of a matrix.
|
||||
*
|
||||
* The returned matrix contains the following information:
|
||||
* - the upper part and lower sub-diagonal represent the Hessenberg matrix H
|
||||
* - the rest of the lower part contains the Householder vectors that, combined with
|
||||
* Householder coefficients returned by householderCoefficients(),
|
||||
* allows to reconstruct the matrix Q as follow:
|
||||
* Q = H_{N-1} ... H_1 H_0
|
||||
* where the matrices H are the Householder transformation:
|
||||
* H_i = (I - h_i * v_i * v_i')
|
||||
* where h_i == householderCoefficients()[i] and v_i is a Householder vector:
|
||||
* v_i = [ 0, ..., 0, 1, M(i+2,i), ..., M(N-1,i) ]
|
||||
* allows to reconstruct the matrix Q as
|
||||
* \f$ Q = H_{N-1} \ldots H_1 H_0 \f$.
|
||||
* Here, the matrices \f$ H_i \f$ are the Householder transformations
|
||||
* \f$ H_i = (I - h_i v_i v_i^T) \f$
|
||||
* where \f$ h_i \f$ is the \f$ i \f$th Householder coefficient and
|
||||
* \f$ v_i \f$ is the Householder vector defined by
|
||||
* \f$ v_i = [ 0, \ldots, 0, 1, M(i+2,i), \ldots, M(N-1,i) ]^T \f$
|
||||
* with M the matrix returned by this function.
|
||||
*
|
||||
* See LAPACK for further details on this packed storage.
|
||||
*
|
||||
* Example: \include HessenbergDecomposition_packedMatrix.cpp
|
||||
* Output: \verbinclude HessenbergDecomposition_packedMatrix.out
|
||||
*
|
||||
* \sa householderCoefficients()
|
||||
*/
|
||||
const MatrixType& packedMatrix(void) const { return m_matrix; }
|
||||
|
||||
/** \brief Reconstructs the orthogonal matrix Q in the decomposition
|
||||
*
|
||||
* \returns the matrix Q
|
||||
*
|
||||
* \pre Either the constructor HessenbergDecomposition(const MatrixType&)
|
||||
* or the member function compute(const MatrixType&) has been called
|
||||
* before to compute the Hessenberg decomposition of a matrix.
|
||||
*
|
||||
* This function reconstructs the matrix Q from the Householder
|
||||
* coefficients and the packed matrix stored internally. This
|
||||
* reconstruction requires \f$ 4n^3 / 3 \f$ flops.
|
||||
*
|
||||
* \sa matrixH() for an example
|
||||
*/
|
||||
MatrixType matrixQ() const;
|
||||
|
||||
/** \brief Constructs the Hessenberg matrix H in the decomposition
|
||||
*
|
||||
* \returns the matrix H
|
||||
*
|
||||
* \pre Either the constructor HessenbergDecomposition(const MatrixType&)
|
||||
* or the member function compute(const MatrixType&) has been called
|
||||
* before to compute the Hessenberg decomposition of a matrix.
|
||||
*
|
||||
* This function copies the matrix H from internal data. The upper part
|
||||
* (including the subdiagonal) of the packed matrix as returned by
|
||||
* packedMatrix() contains the matrix H. This function copies those
|
||||
* entries in a newly created matrix and sets the remaining entries to
|
||||
* zero. It may sometimes be sufficient to directly use the packed matrix
|
||||
* instead of creating a new one.
|
||||
*
|
||||
* Example: \include HessenbergDecomposition_matrixH.cpp
|
||||
* Output: \verbinclude HessenbergDecomposition_matrixH.out
|
||||
*
|
||||
* \sa matrixQ(), packedMatrix()
|
||||
*/
|
||||
MatrixType matrixH() const;
|
||||
|
||||
private:
|
||||
|
||||
static void _compute(MatrixType& matA, CoeffVectorType& hCoeffs);
|
||||
typedef Matrix<Scalar, 1, Size, Options | RowMajor, 1, MaxSize> VectorType;
|
||||
typedef typename NumTraits<Scalar>::Real RealScalar;
|
||||
|
||||
protected:
|
||||
MatrixType m_matrix;
|
||||
@@ -134,7 +245,7 @@ template<typename _MatrixType> class HessenbergDecomposition
|
||||
*
|
||||
* The result is written in the lower triangular part of \a matA.
|
||||
*
|
||||
* Implemented from Golub's "Matrix Computations", algorithm 8.3.1.
|
||||
* Implemented from Golub's "%Matrix Computations", algorithm 8.3.1.
|
||||
*
|
||||
* \sa packedMatrix()
|
||||
*/
|
||||
@@ -167,7 +278,6 @@ void HessenbergDecomposition<MatrixType>::_compute(MatrixType& matA, CoeffVector
|
||||
}
|
||||
}
|
||||
|
||||
/** reconstructs and returns the matrix Q */
|
||||
template<typename MatrixType>
|
||||
typename HessenbergDecomposition<MatrixType>::MatrixType
|
||||
HessenbergDecomposition<MatrixType>::matrixQ() const
|
||||
@@ -185,11 +295,6 @@ HessenbergDecomposition<MatrixType>::matrixQ() const
|
||||
|
||||
#endif // EIGEN_HIDE_HEAVY_CODE
|
||||
|
||||
/** constructs and returns the matrix H.
|
||||
* Note that the matrix H is equivalent to the upper part of the packed matrix
|
||||
* (including the lower sub-diagonal). Therefore, it might be often sufficient
|
||||
* to directly use the packed matrix instead of creating a new one.
|
||||
*/
|
||||
template<typename MatrixType>
|
||||
typename HessenbergDecomposition<MatrixType>::MatrixType
|
||||
HessenbergDecomposition<MatrixType>::matrixH() const
|
||||
|
||||
427
Eigen/src/Eigenvalues/RealSchur.h
Normal file
427
Eigen/src/Eigenvalues/RealSchur.h
Normal file
@@ -0,0 +1,427 @@
|
||||
// This file is part of Eigen, a lightweight C++ template library
|
||||
// for linear algebra.
|
||||
//
|
||||
// Copyright (C) 2008 Gael Guennebaud <g.gael@free.fr>
|
||||
// Copyright (C) 2010 Jitse Niesen <jitse@maths.leeds.ac.uk>
|
||||
//
|
||||
// Eigen is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 3 of the License, or (at your option) any later version.
|
||||
//
|
||||
// Alternatively, you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of
|
||||
// the License, or (at your option) any later version.
|
||||
//
|
||||
// Eigen is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
// FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License or the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License and a copy of the GNU General Public License along with
|
||||
// Eigen. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef EIGEN_REAL_SCHUR_H
|
||||
#define EIGEN_REAL_SCHUR_H
|
||||
|
||||
#include "./HessenbergDecomposition.h"
|
||||
|
||||
/** \eigenvalues_module \ingroup Eigenvalues_Module
|
||||
* \nonstableyet
|
||||
*
|
||||
* \class RealSchur
|
||||
*
|
||||
* \brief Performs a real Schur decomposition of a square matrix
|
||||
*
|
||||
* \tparam _MatrixType the type of the matrix of which we are computing the
|
||||
* real Schur decomposition; this is expected to be an instantiation of the
|
||||
* Matrix class template.
|
||||
*
|
||||
* Given a real square matrix A, this class computes the real Schur
|
||||
* decomposition: \f$ A = U T U^T \f$ where U is a real orthogonal matrix and
|
||||
* T is a real quasi-triangular matrix. An orthogonal matrix is a matrix whose
|
||||
* inverse is equal to its transpose, \f$ U^{-1} = U^T \f$. A quasi-triangular
|
||||
* matrix is a block-triangular matrix whose diagonal consists of 1-by-1
|
||||
* blocks and 2-by-2 blocks with complex eigenvalues. The eigenvalues of the
|
||||
* blocks on the diagonal of T are the same as the eigenvalues of the matrix
|
||||
* A, and thus the real Schur decomposition is used in EigenSolver to compute
|
||||
* the eigendecomposition of a matrix.
|
||||
*
|
||||
* Call the function compute() to compute the real Schur decomposition of a
|
||||
* given matrix. Alternatively, you can use the RealSchur(const MatrixType&)
|
||||
* constructor which computes the real Schur decomposition at construction
|
||||
* time. Once the decomposition is computed, you can use the matrixU() and
|
||||
* matrixT() functions to retrieve the matrices U and T in the decomposition.
|
||||
*
|
||||
* The documentation of RealSchur(const MatrixType&) contains an example of
|
||||
* the typical use of this class.
|
||||
*
|
||||
* \note The implementation is adapted from
|
||||
* <a href="http://math.nist.gov/javanumerics/jama/">JAMA</a> (public domain).
|
||||
* Their code is based on EISPACK.
|
||||
*
|
||||
* \sa class ComplexSchur, class EigenSolver, class ComplexEigenSolver
|
||||
*/
|
||||
template<typename _MatrixType> class RealSchur
|
||||
{
|
||||
public:
|
||||
typedef _MatrixType MatrixType;
|
||||
enum {
|
||||
RowsAtCompileTime = MatrixType::RowsAtCompileTime,
|
||||
ColsAtCompileTime = MatrixType::ColsAtCompileTime,
|
||||
Options = MatrixType::Options,
|
||||
MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime,
|
||||
MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime
|
||||
};
|
||||
typedef typename MatrixType::Scalar Scalar;
|
||||
typedef std::complex<typename NumTraits<Scalar>::Real> ComplexScalar;
|
||||
typedef Matrix<ComplexScalar, ColsAtCompileTime, 1, Options & ~RowMajor, MaxColsAtCompileTime, 1> EigenvalueType;
|
||||
|
||||
/** \brief Default constructor.
|
||||
*
|
||||
* \param [in] size Positive integer, size of the matrix whose Schur decomposition will be computed.
|
||||
*
|
||||
* The default constructor is useful in cases in which the user intends to
|
||||
* perform decompositions via compute(). The \p size parameter is only
|
||||
* used as a hint. It is not an error to give a wrong \p size, but it may
|
||||
* impair performance.
|
||||
*
|
||||
* \sa compute() for an example.
|
||||
*/
|
||||
RealSchur(int size = RowsAtCompileTime==Dynamic ? 1 : RowsAtCompileTime)
|
||||
: m_matT(size, size),
|
||||
m_matU(size, size),
|
||||
m_isInitialized(false)
|
||||
{ }
|
||||
|
||||
/** \brief Constructor; computes real Schur decomposition of given matrix.
|
||||
*
|
||||
* \param[in] matrix Square matrix whose Schur decomposition is to be computed.
|
||||
*
|
||||
* This constructor calls compute() to compute the Schur decomposition.
|
||||
*
|
||||
* Example: \include RealSchur_RealSchur_MatrixType.cpp
|
||||
* Output: \verbinclude RealSchur_RealSchur_MatrixType.out
|
||||
*/
|
||||
RealSchur(const MatrixType& matrix)
|
||||
: m_matT(matrix.rows(),matrix.cols()),
|
||||
m_matU(matrix.rows(),matrix.cols()),
|
||||
m_isInitialized(false)
|
||||
{
|
||||
compute(matrix);
|
||||
}
|
||||
|
||||
/** \brief Returns the orthogonal matrix in the Schur decomposition.
|
||||
*
|
||||
* \returns A const reference to the matrix U.
|
||||
*
|
||||
* \pre Either the constructor RealSchur(const MatrixType&) or the member
|
||||
* function compute(const MatrixType&) has been called before to compute
|
||||
* the Schur decomposition of a matrix.
|
||||
*
|
||||
* \sa RealSchur(const MatrixType&) for an example
|
||||
*/
|
||||
const MatrixType& matrixU() const
|
||||
{
|
||||
ei_assert(m_isInitialized && "RealSchur is not initialized.");
|
||||
return m_matU;
|
||||
}
|
||||
|
||||
/** \brief Returns the quasi-triangular matrix in the Schur decomposition.
|
||||
*
|
||||
* \returns A const reference to the matrix T.
|
||||
*
|
||||
* \pre Either the constructor RealSchur(const MatrixType&) or the member
|
||||
* function compute(const MatrixType&) has been called before to compute
|
||||
* the Schur decomposition of a matrix.
|
||||
*
|
||||
* \sa RealSchur(const MatrixType&) for an example
|
||||
*/
|
||||
const MatrixType& matrixT() const
|
||||
{
|
||||
ei_assert(m_isInitialized && "RealSchur is not initialized.");
|
||||
return m_matT;
|
||||
}
|
||||
|
||||
/** \brief Computes Schur decomposition of given matrix.
|
||||
*
|
||||
* \param[in] matrix Square matrix whose Schur decomposition is to be computed.
|
||||
*
|
||||
* The Schur decomposition is computed by first reducing the matrix to
|
||||
* Hessenberg form using the class HessenbergDecomposition. The Hessenberg
|
||||
* matrix is then reduced to triangular form by performing Francis QR
|
||||
* iterations with implicit double shift. The cost of computing the Schur
|
||||
* decomposition depends on the number of iterations; as a rough guide, it
|
||||
* may be taken to be \f$25n^3\f$ flops.
|
||||
*
|
||||
* Example: \include RealSchur_compute.cpp
|
||||
* Output: \verbinclude RealSchur_compute.out
|
||||
*/
|
||||
void compute(const MatrixType& matrix);
|
||||
|
||||
private:
|
||||
|
||||
MatrixType m_matT;
|
||||
MatrixType m_matU;
|
||||
bool m_isInitialized;
|
||||
|
||||
typedef Matrix<Scalar,3,1> Vector3s;
|
||||
|
||||
Scalar computeNormOfT();
|
||||
int findSmallSubdiagEntry(int iu, Scalar norm);
|
||||
void splitOffTwoRows(int iu, Scalar exshift);
|
||||
void computeShift(int iu, int iter, Scalar& exshift, Vector3s& shiftInfo);
|
||||
void initFrancisQRStep(int il, int iu, const Vector3s& shiftInfo, int& im, Vector3s& firstHouseholderVector);
|
||||
void performFrancisQRStep(int il, int im, int iu, const Vector3s& firstHouseholderVector, Scalar* workspace);
|
||||
};
|
||||
|
||||
|
||||
template<typename MatrixType>
|
||||
void RealSchur<MatrixType>::compute(const MatrixType& matrix)
|
||||
{
|
||||
assert(matrix.cols() == matrix.rows());
|
||||
|
||||
// Step 1. Reduce to Hessenberg form
|
||||
// TODO skip Q if skipU = true
|
||||
HessenbergDecomposition<MatrixType> hess(matrix);
|
||||
m_matT = hess.matrixH();
|
||||
m_matU = hess.matrixQ();
|
||||
|
||||
// Step 2. Reduce to real Schur form
|
||||
typedef Matrix<Scalar, ColsAtCompileTime, 1, Options & ~RowMajor, MaxColsAtCompileTime, 1> ColumnVectorType;
|
||||
ColumnVectorType workspaceVector(m_matU.cols());
|
||||
Scalar* workspace = &workspaceVector.coeffRef(0);
|
||||
|
||||
// The matrix m_matT is divided in three parts.
|
||||
// Rows 0,...,il-1 are decoupled from the rest because m_matT(il,il-1) is zero.
|
||||
// Rows il,...,iu is the part we are working on (the active window).
|
||||
// Rows iu+1,...,end are already brought in triangular form.
|
||||
int iu = m_matU.cols() - 1;
|
||||
int iter = 0; // iteration count
|
||||
Scalar exshift = 0.0; // sum of exceptional shifts
|
||||
Scalar norm = computeNormOfT();
|
||||
|
||||
while (iu >= 0)
|
||||
{
|
||||
int il = findSmallSubdiagEntry(iu, norm);
|
||||
|
||||
// Check for convergence
|
||||
if (il == iu) // One root found
|
||||
{
|
||||
m_matT.coeffRef(iu,iu) = m_matT.coeff(iu,iu) + exshift;
|
||||
if (iu > 0)
|
||||
m_matT.coeffRef(iu, iu-1) = Scalar(0);
|
||||
iu--;
|
||||
iter = 0;
|
||||
}
|
||||
else if (il == iu-1) // Two roots found
|
||||
{
|
||||
splitOffTwoRows(iu, exshift);
|
||||
iu -= 2;
|
||||
iter = 0;
|
||||
}
|
||||
else // No convergence yet
|
||||
{
|
||||
Vector3s firstHouseholderVector, shiftInfo;
|
||||
computeShift(iu, iter, exshift, shiftInfo);
|
||||
iter = iter + 1; // (Could check iteration count here.)
|
||||
int im;
|
||||
initFrancisQRStep(il, iu, shiftInfo, im, firstHouseholderVector);
|
||||
performFrancisQRStep(il, im, iu, firstHouseholderVector, workspace);
|
||||
}
|
||||
}
|
||||
|
||||
m_isInitialized = true;
|
||||
}
|
||||
|
||||
/** \internal Computes and returns vector L1 norm of T */
|
||||
template<typename MatrixType>
|
||||
inline typename MatrixType::Scalar RealSchur<MatrixType>::computeNormOfT()
|
||||
{
|
||||
const int size = m_matU.cols();
|
||||
// FIXME to be efficient the following would requires a triangular reduxion code
|
||||
// Scalar norm = m_matT.upper().cwiseAbs().sum()
|
||||
// + m_matT.corner(BottomLeft,size-1,size-1).diagonal().cwiseAbs().sum();
|
||||
Scalar norm = 0.0;
|
||||
for (int j = 0; j < size; ++j)
|
||||
norm += m_matT.row(j).segment(std::max(j-1,0), size-std::max(j-1,0)).cwiseAbs().sum();
|
||||
return norm;
|
||||
}
|
||||
|
||||
/** \internal Look for single small sub-diagonal element and returns its index */
|
||||
template<typename MatrixType>
|
||||
inline int RealSchur<MatrixType>::findSmallSubdiagEntry(int iu, Scalar norm)
|
||||
{
|
||||
int res = iu;
|
||||
while (res > 0)
|
||||
{
|
||||
Scalar s = ei_abs(m_matT.coeff(res-1,res-1)) + ei_abs(m_matT.coeff(res,res));
|
||||
if (s == 0.0)
|
||||
s = norm;
|
||||
if (ei_abs(m_matT.coeff(res,res-1)) < NumTraits<Scalar>::epsilon() * s)
|
||||
break;
|
||||
res--;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/** \internal Update T given that rows iu-1 and iu decouple from the rest. */
|
||||
template<typename MatrixType>
|
||||
inline void RealSchur<MatrixType>::splitOffTwoRows(int iu, Scalar exshift)
|
||||
{
|
||||
const int size = m_matU.cols();
|
||||
|
||||
// The eigenvalues of the 2x2 matrix [a b; c d] are
|
||||
// trace +/- sqrt(discr/4) where discr = tr^2 - 4*det, tr = a + d, det = ad - bc
|
||||
Scalar p = Scalar(0.5) * (m_matT.coeff(iu-1,iu-1) - m_matT.coeff(iu,iu));
|
||||
Scalar q = p * p + m_matT.coeff(iu,iu-1) * m_matT.coeff(iu-1,iu); // q = tr^2 / 4 - det = discr/4
|
||||
m_matT.coeffRef(iu,iu) += exshift;
|
||||
m_matT.coeffRef(iu-1,iu-1) += exshift;
|
||||
|
||||
if (q >= 0) // Two real eigenvalues
|
||||
{
|
||||
Scalar z = ei_sqrt(ei_abs(q));
|
||||
PlanarRotation<Scalar> rot;
|
||||
if (p >= 0)
|
||||
rot.makeGivens(p + z, m_matT.coeff(iu, iu-1));
|
||||
else
|
||||
rot.makeGivens(p - z, m_matT.coeff(iu, iu-1));
|
||||
|
||||
m_matT.block(0, iu-1, size, size-iu+1).applyOnTheLeft(iu-1, iu, rot.adjoint());
|
||||
m_matT.block(0, 0, iu+1, size).applyOnTheRight(iu-1, iu, rot);
|
||||
m_matT.coeffRef(iu, iu-1) = Scalar(0);
|
||||
m_matU.applyOnTheRight(iu-1, iu, rot);
|
||||
}
|
||||
|
||||
if (iu > 1)
|
||||
m_matT.coeffRef(iu-1, iu-2) = Scalar(0);
|
||||
}
|
||||
|
||||
/** \internal Form shift in shiftInfo, and update exshift if an exceptional shift is performed. */
|
||||
template<typename MatrixType>
|
||||
inline void RealSchur<MatrixType>::computeShift(int iu, int iter, Scalar& exshift, Vector3s& shiftInfo)
|
||||
{
|
||||
shiftInfo.coeffRef(0) = m_matT.coeff(iu,iu);
|
||||
shiftInfo.coeffRef(1) = m_matT.coeff(iu-1,iu-1);
|
||||
shiftInfo.coeffRef(2) = m_matT.coeff(iu,iu-1) * m_matT.coeff(iu-1,iu);
|
||||
|
||||
// Wilkinson's original ad hoc shift
|
||||
if (iter == 10)
|
||||
{
|
||||
exshift += shiftInfo.coeff(0);
|
||||
for (int i = 0; i <= iu; ++i)
|
||||
m_matT.coeffRef(i,i) -= shiftInfo.coeff(0);
|
||||
Scalar s = ei_abs(m_matT.coeff(iu,iu-1)) + ei_abs(m_matT.coeff(iu-1,iu-2));
|
||||
shiftInfo.coeffRef(0) = Scalar(0.75) * s;
|
||||
shiftInfo.coeffRef(1) = Scalar(0.75) * s;
|
||||
shiftInfo.coeffRef(2) = Scalar(-0.4375) * s * s;
|
||||
}
|
||||
|
||||
// MATLAB's new ad hoc shift
|
||||
if (iter == 30)
|
||||
{
|
||||
Scalar s = (shiftInfo.coeff(1) - shiftInfo.coeff(0)) / Scalar(2.0);
|
||||
s = s * s + shiftInfo.coeff(2);
|
||||
if (s > 0)
|
||||
{
|
||||
s = ei_sqrt(s);
|
||||
if (shiftInfo.coeff(1) < shiftInfo.coeff(0))
|
||||
s = -s;
|
||||
s = s + (shiftInfo.coeff(1) - shiftInfo.coeff(0)) / Scalar(2.0);
|
||||
s = shiftInfo.coeff(0) - shiftInfo.coeff(2) / s;
|
||||
exshift += s;
|
||||
for (int i = 0; i <= iu; ++i)
|
||||
m_matT.coeffRef(i,i) -= s;
|
||||
shiftInfo.setConstant(Scalar(0.964));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \internal Compute index im at which Francis QR step starts and the first Householder vector. */
|
||||
template<typename MatrixType>
|
||||
inline void RealSchur<MatrixType>::initFrancisQRStep(int il, int iu, const Vector3s& shiftInfo, int& im, Vector3s& firstHouseholderVector)
|
||||
{
|
||||
Vector3s& v = firstHouseholderVector; // alias to save typing
|
||||
|
||||
for (im = iu-2; im >= il; --im)
|
||||
{
|
||||
const Scalar Tmm = m_matT.coeff(im,im);
|
||||
const Scalar r = shiftInfo.coeff(0) - Tmm;
|
||||
const Scalar s = shiftInfo.coeff(1) - Tmm;
|
||||
v.coeffRef(0) = (r * s - shiftInfo.coeff(2)) / m_matT.coeff(im+1,im) + m_matT.coeff(im,im+1);
|
||||
v.coeffRef(1) = m_matT.coeff(im+1,im+1) - Tmm - r - s;
|
||||
v.coeffRef(2) = m_matT.coeff(im+2,im+1);
|
||||
if (im == il) {
|
||||
break;
|
||||
}
|
||||
const Scalar lhs = m_matT.coeff(im,im-1) * (ei_abs(v.coeff(1)) + ei_abs(v.coeff(2)));
|
||||
const Scalar rhs = v.coeff(0) * (ei_abs(m_matT.coeff(im-1,im-1)) + ei_abs(Tmm) + ei_abs(m_matT.coeff(im+1,im+1)));
|
||||
if (ei_abs(lhs) < NumTraits<Scalar>::epsilon() * rhs)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \internal Perform a Francis QR step involving rows il:iu and columns im:iu. */
|
||||
template<typename MatrixType>
|
||||
inline void RealSchur<MatrixType>::performFrancisQRStep(int il, int im, int iu, const Vector3s& firstHouseholderVector, Scalar* workspace)
|
||||
{
|
||||
assert(im >= il);
|
||||
assert(im <= iu-2);
|
||||
|
||||
const int size = m_matU.cols();
|
||||
|
||||
for (int k = im; k <= iu-2; ++k)
|
||||
{
|
||||
bool firstIteration = (k == im);
|
||||
|
||||
Vector3s v;
|
||||
if (firstIteration)
|
||||
v = firstHouseholderVector;
|
||||
else
|
||||
v = m_matT.template block<3,1>(k,k-1);
|
||||
|
||||
Scalar tau, beta;
|
||||
Matrix<Scalar, 2, 1> ess;
|
||||
v.makeHouseholder(ess, tau, beta);
|
||||
|
||||
if (beta != Scalar(0)) // if v is not zero
|
||||
{
|
||||
if (firstIteration && k > il)
|
||||
m_matT.coeffRef(k,k-1) = -m_matT.coeff(k,k-1);
|
||||
else if (!firstIteration)
|
||||
m_matT.coeffRef(k,k-1) = beta;
|
||||
|
||||
// These Householder transformations form the O(n^3) part of the algorithm
|
||||
m_matT.block(k, k, 3, size-k).applyHouseholderOnTheLeft(ess, tau, workspace);
|
||||
m_matT.block(0, k, std::min(iu,k+3) + 1, 3).applyHouseholderOnTheRight(ess, tau, workspace);
|
||||
m_matU.block(0, k, size, 3).applyHouseholderOnTheRight(ess, tau, workspace);
|
||||
}
|
||||
}
|
||||
|
||||
Matrix<Scalar, 2, 1> v = m_matT.template block<2,1>(iu-1, iu-2);
|
||||
Scalar tau, beta;
|
||||
Matrix<Scalar, 1, 1> ess;
|
||||
v.makeHouseholder(ess, tau, beta);
|
||||
|
||||
if (beta != Scalar(0)) // if v is not zero
|
||||
{
|
||||
m_matT.coeffRef(iu-1, iu-2) = beta;
|
||||
m_matT.block(iu-1, iu-1, 2, size-iu+1).applyHouseholderOnTheLeft(ess, tau, workspace);
|
||||
m_matT.block(0, iu-1, iu+1, 2).applyHouseholderOnTheRight(ess, tau, workspace);
|
||||
m_matU.block(0, iu-1, size, 2).applyHouseholderOnTheRight(ess, tau, workspace);
|
||||
}
|
||||
|
||||
// clean up pollution due to round-off errors
|
||||
for (int i = im+2; i <= iu; ++i)
|
||||
{
|
||||
m_matT.coeffRef(i,i-2) = Scalar(0);
|
||||
if (i > im+2)
|
||||
m_matT.coeffRef(i,i-3) = Scalar(0);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // EIGEN_REAL_SCHUR_H
|
||||
@@ -439,8 +439,7 @@ struct ei_solve_retval<PartialPivLU<_MatrixType>, Rhs>
|
||||
* Step 3: replace c by the solution x to Ux = c.
|
||||
*/
|
||||
|
||||
const int size = dec().matrixLU().rows();
|
||||
ei_assert(rhs().rows() == size);
|
||||
ei_assert(rhs().rows() == dec().matrixLU().rows());
|
||||
|
||||
// Step 1
|
||||
dst = dec().permutationP() * rhs();
|
||||
|
||||
@@ -65,12 +65,12 @@ cholmod_sparse SparseMatrixBase<Derived>::asCholmodMatrix()
|
||||
res.p = derived()._outerIndexPtr();
|
||||
res.i = derived()._innerIndexPtr();
|
||||
res.x = derived()._valuePtr();
|
||||
res.xtype = CHOLMOD_REAL;
|
||||
res.itype = CHOLMOD_INT;
|
||||
res.sorted = 1;
|
||||
res.packed = 1;
|
||||
res.dtype = 0;
|
||||
res.stype = -1;
|
||||
res.xtype = CHOLMOD_REAL;
|
||||
res.itype = CHOLMOD_INT;
|
||||
res.sorted = 1;
|
||||
res.packed = 1;
|
||||
res.dtype = 0;
|
||||
res.stype = -1;
|
||||
|
||||
ei_cholmod_configure_matrix<Scalar>(res);
|
||||
|
||||
@@ -84,7 +84,7 @@ cholmod_sparse SparseMatrixBase<Derived>::asCholmodMatrix()
|
||||
res.stype = 0;
|
||||
}
|
||||
else
|
||||
res.stype = 0;
|
||||
res.stype = -1; // by default we consider the lower part
|
||||
|
||||
return res;
|
||||
}
|
||||
@@ -177,21 +177,21 @@ void SparseLLT<MatrixType,Cholmod>::compute(const MatrixType& a)
|
||||
}
|
||||
|
||||
cholmod_sparse A = const_cast<MatrixType&>(a).asCholmodMatrix();
|
||||
m_cholmod.supernodal = CHOLMOD_AUTO;
|
||||
// m_cholmod.supernodal = CHOLMOD_AUTO;
|
||||
// TODO
|
||||
if (m_flags&IncompleteFactorization)
|
||||
{
|
||||
m_cholmod.nmethods = 1;
|
||||
m_cholmod.method[0].ordering = CHOLMOD_NATURAL;
|
||||
m_cholmod.postorder = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_cholmod.nmethods = 1;
|
||||
m_cholmod.method[0].ordering = CHOLMOD_NATURAL;
|
||||
m_cholmod.postorder = 0;
|
||||
}
|
||||
m_cholmod.final_ll = 1;
|
||||
// if (m_flags&IncompleteFactorization)
|
||||
// {
|
||||
// m_cholmod.nmethods = 1;
|
||||
// m_cholmod.method[0].ordering = CHOLMOD_NATURAL;
|
||||
// m_cholmod.postorder = 0;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// m_cholmod.nmethods = 1;
|
||||
// m_cholmod.method[0].ordering = CHOLMOD_NATURAL;
|
||||
// m_cholmod.postorder = 0;
|
||||
// }
|
||||
// m_cholmod.final_ll = 1;
|
||||
m_cholmodFactor = cholmod_analyze(&A, &m_cholmod);
|
||||
cholmod_factorize(&A, m_cholmodFactor, &m_cholmod);
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ SparseMatrixBase<Derived>::dot(const MatrixBase<OtherDerived>& other) const
|
||||
|
||||
ei_assert(size() == other.size());
|
||||
ei_assert(other.size()>0 && "you are using a non initialized vector");
|
||||
|
||||
|
||||
typename Derived::InnerIterator i(derived(),0);
|
||||
Scalar res = 0;
|
||||
while (i)
|
||||
@@ -59,9 +59,9 @@ SparseMatrixBase<Derived>::dot(const SparseMatrixBase<OtherDerived>& other) cons
|
||||
EIGEN_STATIC_ASSERT_SAME_VECTOR_SIZE(Derived,OtherDerived)
|
||||
EIGEN_STATIC_ASSERT((ei_is_same_type<Scalar, typename OtherDerived::Scalar>::ret),
|
||||
YOU_MIXED_DIFFERENT_NUMERIC_TYPES__YOU_NEED_TO_USE_THE_CAST_METHOD_OF_MATRIXBASE_TO_CAST_NUMERIC_TYPES_EXPLICITLY)
|
||||
|
||||
|
||||
ei_assert(size() == other.size());
|
||||
|
||||
|
||||
typename Derived::InnerIterator i(derived(),0);
|
||||
typename OtherDerived::InnerIterator j(other.derived(),0);
|
||||
Scalar res = 0;
|
||||
@@ -84,7 +84,7 @@ template<typename Derived>
|
||||
inline typename NumTraits<typename ei_traits<Derived>::Scalar>::Real
|
||||
SparseMatrixBase<Derived>::squaredNorm() const
|
||||
{
|
||||
return ei_real((*this).cwise().abs2().sum());
|
||||
return ei_real((*this).cwiseAbs2().sum());
|
||||
}
|
||||
|
||||
template<typename Derived>
|
||||
|
||||
@@ -80,6 +80,7 @@ class SparseLLT<MatrixType,Taucs> : public SparseLLT<MatrixType>
|
||||
typedef SparseLLT<MatrixType> Base;
|
||||
typedef typename Base::Scalar Scalar;
|
||||
typedef typename Base::RealScalar RealScalar;
|
||||
typedef typename Base::CholMatrixType CholMatrixType;
|
||||
using Base::MatrixLIsDirty;
|
||||
using Base::SupernodalFactorIsDirty;
|
||||
using Base::m_flags;
|
||||
@@ -88,12 +89,12 @@ class SparseLLT<MatrixType,Taucs> : public SparseLLT<MatrixType>
|
||||
|
||||
public:
|
||||
|
||||
SparseLLT(int flags = 0)
|
||||
SparseLLT(int flags = SupernodalMultifrontal)
|
||||
: Base(flags), m_taucsSupernodalFactor(0)
|
||||
{
|
||||
}
|
||||
|
||||
SparseLLT(const MatrixType& matrix, int flags = 0)
|
||||
SparseLLT(const MatrixType& matrix, int flags = SupernodalMultifrontal)
|
||||
: Base(flags), m_taucsSupernodalFactor(0)
|
||||
{
|
||||
compute(matrix);
|
||||
@@ -105,7 +106,7 @@ class SparseLLT<MatrixType,Taucs> : public SparseLLT<MatrixType>
|
||||
taucs_supernodal_factor_free(m_taucsSupernodalFactor);
|
||||
}
|
||||
|
||||
inline const typename Base::CholMatrixType& matrixL(void) const;
|
||||
inline const CholMatrixType& matrixL() const;
|
||||
|
||||
template<typename Derived>
|
||||
void solveInPlace(MatrixBase<Derived> &b) const;
|
||||
@@ -156,7 +157,7 @@ void SparseLLT<MatrixType,Taucs>::compute(const MatrixType& a)
|
||||
}
|
||||
|
||||
template<typename MatrixType>
|
||||
inline const typename SparseLLT<MatrixType>::CholMatrixType&
|
||||
inline const typename SparseLLT<MatrixType,Taucs>::CholMatrixType&
|
||||
SparseLLT<MatrixType,Taucs>::matrixL() const
|
||||
{
|
||||
if (m_status & MatrixLIsDirty)
|
||||
|
||||
Reference in New Issue
Block a user