From 9005eb07884ff850b8801cfcb78d3533bace4342 Mon Sep 17 00:00:00 2001 From: Gael Guennebaud Date: Mon, 31 Aug 2009 09:32:46 +0200 Subject: compilation fix in AmbiVector --- Eigen/src/Sparse/AmbiVector.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Eigen/src/Sparse/AmbiVector.h b/Eigen/src/Sparse/AmbiVector.h index 974e5c6c4..474626848 100644 --- a/Eigen/src/Sparse/AmbiVector.h +++ b/Eigen/src/Sparse/AmbiVector.h @@ -41,7 +41,7 @@ template class AmbiVector resize(size); } - void init(RealScalar estimatedDensity); + void init(double estimatedDensity); void init(int mode); int nonZeros() const; @@ -143,7 +143,7 @@ int AmbiVector::nonZeros() const } template -void AmbiVector::init(RealScalar estimatedDensity) +void AmbiVector::init(double estimatedDensity) { if (estimatedDensity>0.1) init(IsDense); -- cgit v1.2.3 From 99bfab6dcfec005bc2bc9291716fb3a7c5e7c21d Mon Sep 17 00:00:00 2001 From: Hauke Heibel Date: Mon, 31 Aug 2009 13:47:32 +0200 Subject: Removed redundant assignment operators. --- Eigen/src/Core/MapBase.h | 5 ----- Eigen/src/Core/util/Macros.h | 6 +----- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/Eigen/src/Core/MapBase.h b/Eigen/src/Core/MapBase.h index 9cb085b61..a8fded4a0 100644 --- a/Eigen/src/Core/MapBase.h +++ b/Eigen/src/Core/MapBase.h @@ -166,11 +166,6 @@ template class MapBase && cols >= 0 && (ColsAtCompileTime == Dynamic || ColsAtCompileTime == cols))); } - Derived& operator=(const MapBase& other) - { - return Base::operator=(other); - } - using Base::operator=; using Base::operator*=; diff --git a/Eigen/src/Core/util/Macros.h b/Eigen/src/Core/util/Macros.h index dc3b3ee0a..7fb10a315 100644 --- a/Eigen/src/Core/util/Macros.h +++ b/Eigen/src/Core/util/Macros.h @@ -251,11 +251,7 @@ using Base::operator =; \ using Base::operator +=; \ using Base::operator -=; \ using Base::operator *=; \ -using Base::operator /=; \ -EIGEN_STRONG_INLINE Derived& operator=(const Derived& other) \ -{ \ - return Base::operator=(other); \ -} +using Base::operator /=; #define _EIGEN_GENERIC_PUBLIC_INTERFACE(Derived, BaseClass) \ typedef BaseClass Base; \ -- cgit v1.2.3 From 32a9aee2861bbdeabc2c12d03d7a3337d4e691cd Mon Sep 17 00:00:00 2001 From: Hauke Heibel Date: Mon, 31 Aug 2009 14:40:53 +0200 Subject: Added MSVC guards to assignment operators. --- Eigen/src/Core/MapBase.h | 7 +++++++ Eigen/src/Core/util/Macros.h | 13 +++++++++++++ 2 files changed, 20 insertions(+) diff --git a/Eigen/src/Core/MapBase.h b/Eigen/src/Core/MapBase.h index a8fded4a0..a0311ffcf 100644 --- a/Eigen/src/Core/MapBase.h +++ b/Eigen/src/Core/MapBase.h @@ -166,6 +166,13 @@ template class MapBase && cols >= 0 && (ColsAtCompileTime == Dynamic || ColsAtCompileTime == cols))); } +#ifndef _MSC_VER + Derived& operator=(const MapBase& other) + { + return Base::operator=(other); + } +#endif + using Base::operator=; using Base::operator*=; diff --git a/Eigen/src/Core/util/Macros.h b/Eigen/src/Core/util/Macros.h index 7fb10a315..4e00df759 100644 --- a/Eigen/src/Core/util/Macros.h +++ b/Eigen/src/Core/util/Macros.h @@ -246,12 +246,25 @@ using Eigen::ei_cos; // needed to define it here as escaping characters in CMake add_definition's argument seems very problematic. #define EIGEN_DOCS_IO_FORMAT IOFormat(3, 0, " ", "\n", "", "") +#ifdef _MSC_VER #define EIGEN_INHERIT_ASSIGNMENT_OPERATORS(Derived) \ using Base::operator =; \ using Base::operator +=; \ using Base::operator -=; \ using Base::operator *=; \ using Base::operator /=; +#else +#define EIGEN_INHERIT_ASSIGNMENT_OPERATORS(Derived) \ +using Base::operator =; \ +using Base::operator +=; \ +using Base::operator -=; \ +using Base::operator *=; \ +using Base::operator /=; \ +EIGEN_STRONG_INLINE Derived& operator=(const Derived& other) \ +{ \ + return Base::operator=(other); \ +} +#endif #define _EIGEN_GENERIC_PUBLIC_INTERFACE(Derived, BaseClass) \ typedef BaseClass Base; \ -- cgit v1.2.3 From 0a0a805569402b1761fbe6f046c1f2699636a91f Mon Sep 17 00:00:00 2001 From: Hauke Heibel Date: Mon, 31 Aug 2009 15:34:57 +0200 Subject: Fixed a cast warning in scaleAndAddTo. Fixed lazyness in umeyama. Added a few missing casts. --- Eigen/src/Core/Product.h | 4 ++-- Eigen/src/Geometry/Umeyama.h | 10 +++++----- test/product_trsm.cpp | 4 ++-- test/umeyama.cpp | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Eigen/src/Core/Product.h b/Eigen/src/Core/Product.h index 71203a362..f4c8af6ea 100644 --- a/Eigen/src/Core/Product.h +++ b/Eigen/src/Core/Product.h @@ -259,8 +259,8 @@ class GeneralProduct template void scaleAndAddTo(Dest& dst, Scalar alpha) const { ei_assert(m_lhs.rows() == dst.rows() && m_rhs.cols() == dst.cols()); - ei_gemv_selector::ActualAccess>::run(*this, dst, alpha); + ei_gemv_selector::ActualAccess)>::run(*this, dst, alpha); } }; diff --git a/Eigen/src/Geometry/Umeyama.h b/Eigen/src/Geometry/Umeyama.h index 64c06fe66..7652aa92e 100644 --- a/Eigen/src/Geometry/Umeyama.h +++ b/Eigen/src/Geometry/Umeyama.h @@ -144,7 +144,7 @@ umeyama(const MatrixBase& src, const MatrixBase& dst, boo // const Scalar dst_var = dst_demean.rowwise().squaredNorm().sum() * one_over_n; // Eq. (38) - const MatrixType sigma = (one_over_n * dst_demean * src_demean.transpose()).lazy(); + const MatrixType sigma = one_over_n * dst_demean * src_demean.transpose(); SVD svd(sigma); @@ -160,14 +160,14 @@ umeyama(const MatrixBase& src, const MatrixBase& dst, boo int rank = 0; for (int i=0; i 0 ) { - Rt.block(0,0,m,m) = (svd.matrixU()*svd.matrixV().transpose()).lazy(); + Rt.block(0,0,m,m).noalias() = svd.matrixU()*svd.matrixV().transpose(); } else { const Scalar s = S(m-1); S(m-1) = -1; - Rt.block(0,0,m,m) = (svd.matrixU() * S.asDiagonal() * svd.matrixV().transpose()).lazy(); + Rt.block(0,0,m,m).noalias() = svd.matrixU() * S.asDiagonal() * svd.matrixV().transpose(); S(m-1) = s; } } else { - Rt.block(0,0,m,m) = (svd.matrixU() * S.asDiagonal() * svd.matrixV().transpose()).lazy(); + Rt.block(0,0,m,m).noalias() = svd.matrixU() * S.asDiagonal() * svd.matrixV().transpose(); } // Eq. (42) @@ -177,7 +177,7 @@ umeyama(const MatrixBase& src, const MatrixBase& dst, boo // Note that we first assign dst_mean to the destination so that there no need // for a temporary. Rt.col(m).start(m) = dst_mean; - Rt.col(m).start(m) -= (c*Rt.corner(TopLeft,m,m)*src_mean).lazy(); + Rt.col(m).start(m).noalias() -= c*Rt.corner(TopLeft,m,m)*src_mean; if (with_scaling) Rt.block(0,0,m,m) *= c; diff --git a/test/product_trsm.cpp b/test/product_trsm.cpp index 9f372df91..756034df9 100644 --- a/test/product_trsm.cpp +++ b/test/product_trsm.cpp @@ -40,8 +40,8 @@ template void trsm(int size,int cols) Matrix cmRhs(size,cols), ref(size,cols); Matrix rmRhs(size,cols); - cmLhs.setRandom(); cmLhs *= 0.1; cmLhs.diagonal().cwise() += 1; - rmLhs.setRandom(); rmLhs *= 0.1; rmLhs.diagonal().cwise() += 1; + cmLhs.setRandom(); cmLhs *= static_cast(0.1); cmLhs.diagonal().cwise() += static_cast(1); + rmLhs.setRandom(); rmLhs *= static_cast(0.1); rmLhs.diagonal().cwise() += static_cast(1); VERIFY_TRSM(cmLhs.conjugate().template triangularView(), cmRhs); VERIFY_TRSM(cmLhs .template triangularView(), cmRhs); diff --git a/test/umeyama.cpp b/test/umeyama.cpp index b6d394883..0999c59c9 100644 --- a/test/umeyama.cpp +++ b/test/umeyama.cpp @@ -127,7 +127,7 @@ void run_test(int dim, int num_elements) MatrixX src = MatrixX::Random(dim+1, num_elements); src.row(dim) = Matrix::Constant(num_elements, Scalar(1)); - MatrixX dst = (cR_t*src).lazy(); + MatrixX dst = cR_t*src; MatrixX cR_t_umeyama = umeyama(src.block(0,0,dim,num_elements), dst.block(0,0,dim,num_elements)); @@ -160,7 +160,7 @@ void run_fixed_size_test(int num_elements) MatrixX src = MatrixX::Random(dim+1, num_elements); src.row(dim) = Matrix::Constant(num_elements, Scalar(1)); - MatrixX dst = (cR_t*src).lazy(); + MatrixX dst = cR_t*src; Block src_block(src,0,0,dim,num_elements); Block dst_block(dst,0,0,dim,num_elements); -- cgit v1.2.3 From 27c9ecc50f2d6514a0cc570edecb982162fdca37 Mon Sep 17 00:00:00 2001 From: Gael Guennebaud Date: Mon, 31 Aug 2009 16:41:13 +0200 Subject: fix copy/paste issue --- Eigen/src/Core/CwiseUnaryView.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Eigen/src/Core/CwiseUnaryView.h b/Eigen/src/Core/CwiseUnaryView.h index 4785f01f4..4b7d87953 100644 --- a/Eigen/src/Core/CwiseUnaryView.h +++ b/Eigen/src/Core/CwiseUnaryView.h @@ -53,8 +53,7 @@ struct ei_traits > }; template -class CwiseUnaryView : ei_no_assignment_operator, - public MatrixBase > +class CwiseUnaryView : public MatrixBase > { public: -- cgit v1.2.3 From 095809edda92cb149cf3f558f2263a8dd3881da8 Mon Sep 17 00:00:00 2001 From: Gael Guennebaud Date: Mon, 31 Aug 2009 17:07:54 +0200 Subject: fix issue #45 and document the .data() and .stride() functions --- Eigen/src/Core/Block.h | 16 +++++++++++++++- Eigen/src/Core/MapBase.h | 20 +++++++++++++++++--- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/Eigen/src/Core/Block.h b/Eigen/src/Core/Block.h index cf7730170..42d6bc3c3 100644 --- a/Eigen/src/Core/Block.h +++ b/Eigen/src/Core/Block.h @@ -201,6 +201,13 @@ template && startCol >= 0 && blockCols >= 0 && startCol + blockCols <= matrix.cols()); } - inline int stride(void) const { return m_matrix.stride(); } + /** \sa MapBase::stride() */ + inline int stride() const + { + return ((!Base::IsVectorAtCompileTime) + || (BlockRows==1 && ((Flags&RowMajorBit)==0)) + || (BlockCols==1 && ((Flags&RowMajorBit)==RowMajorBit))) + ? m_matrix.stride() : 1; + } #ifndef __SUNPRO_CC // FIXME sunstudio is not friendly with the above friend... diff --git a/Eigen/src/Core/MapBase.h b/Eigen/src/Core/MapBase.h index a0311ffcf..404fa176e 100644 --- a/Eigen/src/Core/MapBase.h +++ b/Eigen/src/Core/MapBase.h @@ -62,7 +62,21 @@ template class MapBase inline int rows() const { return m_rows.value(); } inline int cols() const { return m_cols.value(); } + /** Returns the leading dimension (for matrices) or the increment (for vectors) to be used with data(). + * + * More precisely: + * - for a column major matrix it returns the number of elements between two successive columns + * - for a row major matrix it returns the number of elements between two successive rows + * - for a vector it returns the number of elements between two successive coefficients + * This function has to be used together with the MapBase::data() function. + * + * \sa MapBase::data() */ inline int stride() const { return derived().stride(); } + + /** Returns a pointer to the first coefficient of the matrix or vector. + * This function has to be used together with the stride() function. + * + * \sa MapBase::stride() */ inline const Scalar* data() const { return m_data; } template struct force_aligned_impl { @@ -167,9 +181,9 @@ template class MapBase } #ifndef _MSC_VER - Derived& operator=(const MapBase& other) - { - return Base::operator=(other); + Derived& operator=(const MapBase& other) + { + return Base::operator=(other); } #endif -- cgit v1.2.3 From bc7aec0ef5475984edc39f43fcb099af44993081 Mon Sep 17 00:00:00 2001 From: Hauke Heibel Date: Mon, 31 Aug 2009 17:24:38 +0200 Subject: ifdef removed from MapBase and warning disabled --- Eigen/src/Core/MapBase.h | 2 -- Eigen/src/Core/util/DisableMSVCWarnings.h | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/Eigen/src/Core/MapBase.h b/Eigen/src/Core/MapBase.h index 404fa176e..88a3fac1e 100644 --- a/Eigen/src/Core/MapBase.h +++ b/Eigen/src/Core/MapBase.h @@ -180,12 +180,10 @@ template class MapBase && cols >= 0 && (ColsAtCompileTime == Dynamic || ColsAtCompileTime == cols))); } -#ifndef _MSC_VER Derived& operator=(const MapBase& other) { return Base::operator=(other); } -#endif using Base::operator=; using Base::operator*=; diff --git a/Eigen/src/Core/util/DisableMSVCWarnings.h b/Eigen/src/Core/util/DisableMSVCWarnings.h index 765ddecc5..ca8e245b3 100644 --- a/Eigen/src/Core/util/DisableMSVCWarnings.h +++ b/Eigen/src/Core/util/DisableMSVCWarnings.h @@ -1,5 +1,5 @@ #ifdef _MSC_VER #pragma warning( push ) - #pragma warning( disable : 4181 4244 4127 4211 4717 ) + #pragma warning( disable : 4181 4244 4127 4211 4522 4717 ) #endif -- cgit v1.2.3 From ab6eb6a1a49124b41b2764be98ac5b07a74a2a41 Mon Sep 17 00:00:00 2001 From: Hauke Heibel Date: Mon, 31 Aug 2009 17:29:37 +0200 Subject: Adaptions from .lazy() towards .noalias(). Added missing casts. --- Eigen/src/Cholesky/LDLT.h | 4 +- Eigen/src/Cholesky/LLT.h | 2 +- Eigen/src/QR/EigenSolver.h | 12 +++--- test/bandmatrix.cpp | 12 +++--- test/product.h | 12 +++--- test/product_symm.cpp | 2 +- .../Eigen/src/MatrixFunctions/MatrixExponential.h | 44 +++++++++++----------- unsupported/test/matrixExponential.cpp | 18 +++++---- 8 files changed, 54 insertions(+), 52 deletions(-) diff --git a/Eigen/src/Cholesky/LDLT.h b/Eigen/src/Cholesky/LDLT.h index adca9fe2e..c8d92f3c0 100644 --- a/Eigen/src/Cholesky/LDLT.h +++ b/Eigen/src/Cholesky/LDLT.h @@ -218,8 +218,8 @@ LDLT& LDLT::compute(const MatrixType& a) int endSize = size - j - 1; if (endSize > 0) { - _temporary.end(endSize) = ( m_matrix.block(j+1,0, endSize, j) - * m_matrix.col(j).start(j).conjugate() ).lazy(); + _temporary.end(endSize).noalias() = m_matrix.block(j+1,0, endSize, j) + * m_matrix.col(j).start(j).conjugate(); m_matrix.row(j).end(endSize) = m_matrix.row(j).end(endSize).conjugate() - _temporary.end(endSize).transpose(); diff --git a/Eigen/src/Cholesky/LLT.h b/Eigen/src/Cholesky/LLT.h index 3ce85b2d9..ec7b8123c 100644 --- a/Eigen/src/Cholesky/LLT.h +++ b/Eigen/src/Cholesky/LLT.h @@ -141,7 +141,7 @@ template<> struct ei_llt_inplace if (x<=RealScalar(0)) return false; mat.coeffRef(k,k) = x = ei_sqrt(x); - if (k>0 && rs>0) A21 -= (A20 * A10.adjoint()).lazy(); + if (k>0 && rs>0) A21.noalias() -= A20 * A10.adjoint(); if (rs>0) A21 *= RealScalar(1)/x; } return true; diff --git a/Eigen/src/QR/EigenSolver.h b/Eigen/src/QR/EigenSolver.h index bd7a76c49..79d73db7e 100644 --- a/Eigen/src/QR/EigenSolver.h +++ b/Eigen/src/QR/EigenSolver.h @@ -244,11 +244,11 @@ void EigenSolver::orthes(MatrixType& matH, RealVectorType& ort) // 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) -= ((ort.segment(m, bSize)/h) - * (ort.segment(m, bSize).transpose() * matH.block(m, m, bSize, n-m))).lazy(); + 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) -= ((matH.block(0, m, high+1, bSize) * ort.segment(m, bSize)) - * (ort.segment(m, bSize)/h).transpose()).lazy(); + 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; @@ -265,8 +265,8 @@ void EigenSolver::orthes(MatrixType& matH, RealVectorType& ort) 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) += ( (ort.segment(m, bSize) / (matH.coeff(m,m-1) * ort.coeff(m))) - * (ort.segment(m, bSize).transpose() * m_eivec.block(m, m, bSize, bSize)) ).lazy(); + 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)) ); } } } diff --git a/test/bandmatrix.cpp b/test/bandmatrix.cpp index f677e7b85..2bdc67e28 100644 --- a/test/bandmatrix.cpp +++ b/test/bandmatrix.cpp @@ -44,21 +44,21 @@ template void bandmatrix(const MatrixType& _m) dm1.diagonal().setConstant(123); for (int i=1; i<=m.supers();++i) { - m.diagonal(i).setConstant(i); - dm1.diagonal(i).setConstant(i); + m.diagonal(i).setConstant(static_cast(i)); + dm1.diagonal(i).setConstant(static_cast(i)); } for (int i=1; i<=m.subs();++i) { - m.diagonal(-i).setConstant(-i); - dm1.diagonal(-i).setConstant(-i); + m.diagonal(-i).setConstant(-static_cast(i)); + dm1.diagonal(-i).setConstant(-static_cast(i)); } //std::cerr << m.m_data << "\n\n" << m.toDense() << "\n\n" << dm1 << "\n\n\n\n"; VERIFY_IS_APPROX(dm1,m.toDense()); for (int i=0; i(i+1)); + dm1.col(i).setConstant(static_cast(i+1)); } int d = std::min(rows,cols); int a = std::max(0,cols-d-supers); diff --git a/test/product.h b/test/product.h index 157f6262b..40773ad90 100644 --- a/test/product.h +++ b/test/product.h @@ -81,7 +81,7 @@ template void product(const MatrixType& m) m3 = m1; m3 *= m1.transpose() * m2; VERIFY_IS_APPROX(m3, m1 * (m1.transpose()*m2)); - VERIFY_IS_APPROX(m3, m1.lazy() * (m1.transpose()*m2)); + VERIFY_IS_APPROX(m3, m1 * (m1.transpose()*m2)); // continue testing Product.h: distributivity VERIFY_IS_APPROX(square*(m1 + m2), square*m1+square*m2); @@ -109,26 +109,26 @@ template void product(const MatrixType& m) // test optimized operator+= path res = square; - res += (m1 * m2.transpose()).lazy(); + res.noalias() += m1 * m2.transpose(); VERIFY_IS_APPROX(res, square + m1 * m2.transpose()); if (NumTraits::HasFloatingPoint && std::min(rows,cols)>1) { VERIFY(areNotApprox(res,square + m2 * m1.transpose())); } vcres = vc2; - vcres += (m1.transpose() * v1).lazy(); + vcres.noalias() += m1.transpose() * v1; VERIFY_IS_APPROX(vcres, vc2 + m1.transpose() * v1); // test optimized operator-= path res = square; - res -= (m1 * m2.transpose()).lazy(); + res.noalias() -= m1 * m2.transpose(); VERIFY_IS_APPROX(res, square - (m1 * m2.transpose())); if (NumTraits::HasFloatingPoint && std::min(rows,cols)>1) { VERIFY(areNotApprox(res,square - m2 * m1.transpose())); } vcres = vc2; - vcres -= (m1.transpose() * v1).lazy(); + vcres.noalias() -= m1.transpose() * v1; VERIFY_IS_APPROX(vcres, vc2 - m1.transpose() * v1); tm1 = m1; @@ -145,7 +145,7 @@ template void product(const MatrixType& m) VERIFY_IS_APPROX(res, m1 * m2.transpose()); res2 = square2; - res2 += (m1.transpose() * m2).lazy(); + res2.noalias() += m1.transpose() * m2; VERIFY_IS_APPROX(res2, square2 + m1.transpose() * m2); if (NumTraits::HasFloatingPoint && std::min(rows,cols)>1) { diff --git a/test/product_symm.cpp b/test/product_symm.cpp index 1300928a2..cf0299c64 100644 --- a/test/product_symm.cpp +++ b/test/product_symm.cpp @@ -96,7 +96,7 @@ template void symm(int size = Size, in m2 = m1.template triangularView(); rhs13 = rhs12; - VERIFY_IS_APPROX(rhs12 += (s1 * ((m2.adjoint()).template selfadjointView() * (s2*rhs3).conjugate())).lazy(), + VERIFY_IS_APPROX(rhs12.noalias() += s1 * ((m2.adjoint()).template selfadjointView() * (s2*rhs3).conjugate()), rhs13 += (s1*m1.adjoint()) * (s2*rhs3).conjugate()); // test matrix * selfadjoint diff --git a/unsupported/Eigen/src/MatrixFunctions/MatrixExponential.h b/unsupported/Eigen/src/MatrixFunctions/MatrixExponential.h index dd25d7f3d..7d8777de7 100644 --- a/unsupported/Eigen/src/MatrixFunctions/MatrixExponential.h +++ b/unsupported/Eigen/src/MatrixFunctions/MatrixExponential.h @@ -90,9 +90,9 @@ namespace MatrixExponentialInternal { { typedef typename ei_traits::Scalar Scalar; const Scalar b[] = {120., 60., 12., 1.}; - M2 = (M * M).lazy(); + M2.noalias() = M * M; tmp = b[3]*M2 + b[1]*Id; - U = (M * tmp).lazy(); + U.noalias() = M * tmp; V = b[2]*M2 + b[0]*Id; } @@ -115,10 +115,10 @@ namespace MatrixExponentialInternal { { typedef typename ei_traits::Scalar Scalar; const Scalar b[] = {30240., 15120., 3360., 420., 30., 1.}; - M2 = (M * M).lazy(); - MatrixType M4 = (M2 * M2).lazy(); + M2.noalias() = M * M; + MatrixType M4 = M2 * M2; tmp = b[5]*M4 + b[3]*M2 + b[1]*Id; - U = (M * tmp).lazy(); + U.noalias() = M * tmp; V = b[4]*M4 + b[2]*M2 + b[0]*Id; } @@ -141,11 +141,11 @@ namespace MatrixExponentialInternal { { typedef typename ei_traits::Scalar Scalar; const Scalar b[] = {17297280., 8648640., 1995840., 277200., 25200., 1512., 56., 1.}; - M2 = (M * M).lazy(); - MatrixType M4 = (M2 * M2).lazy(); - MatrixType M6 = (M4 * M2).lazy(); + M2.noalias() = M * M; + MatrixType M4 = M2 * M2; + MatrixType M6 = M4 * M2; tmp = b[7]*M6 + b[5]*M4 + b[3]*M2 + b[1]*Id; - U = (M * tmp).lazy(); + U.noalias() = M * tmp; V = b[6]*M6 + b[4]*M4 + b[2]*M2 + b[0]*Id; } @@ -169,12 +169,12 @@ namespace MatrixExponentialInternal { typedef typename ei_traits::Scalar Scalar; const Scalar b[] = {17643225600., 8821612800., 2075673600., 302702400., 30270240., 2162160., 110880., 3960., 90., 1.}; - M2 = (M * M).lazy(); - MatrixType M4 = (M2 * M2).lazy(); - MatrixType M6 = (M4 * M2).lazy(); - MatrixType M8 = (M6 * M2).lazy(); + M2.noalias() = M * M; + MatrixType M4 = M2 * M2; + MatrixType M6 = M4 * M2; + MatrixType M8 = M6 * M2; tmp = b[9]*M8 + b[7]*M6 + b[5]*M4 + b[3]*M2 + b[1]*Id; - U = (M * tmp).lazy(); + U.noalias() = M * tmp; V = b[8]*M8 + b[6]*M6 + b[4]*M4 + b[2]*M2 + b[0]*Id; } @@ -199,15 +199,15 @@ namespace MatrixExponentialInternal { const Scalar b[] = {64764752532480000., 32382376266240000., 7771770303897600., 1187353796428800., 129060195264000., 10559470521600., 670442572800., 33522128640., 1323241920., 40840800., 960960., 16380., 182., 1.}; - M2 = (M * M).lazy(); - MatrixType M4 = (M2 * M2).lazy(); - MatrixType M6 = (M4 * M2).lazy(); + M2.noalias() = M * M; + MatrixType M4 = M2 * M2; + MatrixType M6 = M4 * M2; V = b[13]*M6 + b[11]*M4 + b[9]*M2; - tmp = (M6 * V).lazy(); + tmp.noalias() = M6 * V; tmp += b[7]*M6 + b[5]*M4 + b[3]*M2 + b[1]*Id; - U = (M * tmp).lazy(); + U.noalias() = M * tmp; tmp = b[12]*M6 + b[10]*M4 + b[8]*M2; - V = (M6 * tmp).lazy(); + V.noalias() = M6 * tmp; V += b[6]*M6 + b[4]*M4 + b[2]*M2 + b[0]*Id; } @@ -252,7 +252,7 @@ namespace MatrixExponentialInternal { } else if (l1norm < 1.880152677804762e+000) { pade5(M, Id, tmp1, tmp2, U, V); } else { - const float maxnorm = 3.925724783138660; + const float maxnorm = 3.925724783138660f; *squarings = std::max(0, (int)ceil(log2(l1norm / maxnorm))); MatrixType A = M / std::pow(typename ei_traits::Scalar(2), *squarings); pade7(A, Id, tmp1, tmp2, U, V); @@ -294,7 +294,7 @@ namespace MatrixExponentialInternal { { MatrixType num, den, U, V; MatrixType Id = MatrixType::Identity(M.rows(), M.cols()); - float l1norm = M.cwise().abs().colwise().sum().maxCoeff(); + float l1norm = static_cast(M.cwise().abs().colwise().sum().maxCoeff()); int squarings; computeUV_selector::run(M, Id, num, den, U, V, l1norm, &squarings); num = U + V; // numerator of Pade approximant diff --git a/unsupported/test/matrixExponential.cpp b/unsupported/test/matrixExponential.cpp index 9a6c335d8..7d65a701a 100644 --- a/unsupported/test/matrixExponential.cpp +++ b/unsupported/test/matrixExponential.cpp @@ -43,10 +43,10 @@ void test2dRotation(double tol) A << 0, 1, -1, 0; for (int i=0; i<=20; i++) { - angle = pow(10, i / 5. - 2); + angle = static_cast(pow(10, i / 5. - 2)); B << cos(angle), sin(angle), -sin(angle), cos(angle); ei_matrix_exponential(angle*A, &C); - VERIFY(C.isApprox(B, tol)); + VERIFY(C.isApprox(B, static_cast(tol))); } } @@ -59,13 +59,13 @@ void test2dHyperbolicRotation(double tol) for (int i=0; i<=20; i++) { - angle = (i-10) / 2.0; + angle = static_cast((i-10) / 2.0); ch = std::cosh(angle); sh = std::sinh(angle); A << 0, angle*imagUnit, -angle*imagUnit, 0; B << ch, sh*imagUnit, -sh*imagUnit, ch; ei_matrix_exponential(A, &C); - VERIFY(C.isApprox(B, tol)); + VERIFY(C.isApprox(B, static_cast(tol))); } } @@ -77,13 +77,13 @@ void testPascal(double tol) Matrix A(size,size), B(size,size), C(size,size); A.setZero(); for (int i=0; i(i+1); B.setZero(); for (int i=0; i(binom(i,j)); ei_matrix_exponential(A, &C); - VERIFY(C.isApprox(B, tol)); + VERIFY(C.isApprox(B, static_cast(tol))); } } @@ -98,11 +98,13 @@ void randomTest(const MatrixType& m, double tol) MatrixType m1(rows, cols), m2(rows, cols), m3(rows, cols), identity = MatrixType::Identity(rows, rows); + typedef typename NumTraits::Scalar>::Real RealScalar; + for(int i = 0; i < g_repeat; i++) { m1 = MatrixType::Random(rows, cols); ei_matrix_exponential(m1, &m2); ei_matrix_exponential(-m1, &m3); - VERIFY(identity.isApprox(m2 * m3, tol)); + VERIFY(identity.isApprox(m2 * m3, static_cast(tol))); } } -- cgit v1.2.3 From a16599751f42242a3cbb80a00cddc983a6bb2675 Mon Sep 17 00:00:00 2001 From: Gael Guennebaud Date: Mon, 31 Aug 2009 17:39:56 +0200 Subject: fix Matrix::stride for vectors, add a unit test for Block::stride and make use of it where it was relevant --- Eigen/src/Core/Matrix.h | 17 +++++++-- Eigen/src/Core/products/SelfadjointMatrixVector.h | 12 +++--- test/submatrices.cpp | 45 +++++++++++++++++++++++ 3 files changed, 64 insertions(+), 10 deletions(-) diff --git a/Eigen/src/Core/Matrix.h b/Eigen/src/Core/Matrix.h index f58424ba2..d0603871a 100644 --- a/Eigen/src/Core/Matrix.h +++ b/Eigen/src/Core/Matrix.h @@ -142,12 +142,21 @@ class Matrix EIGEN_STRONG_INLINE int rows() const { return m_storage.rows(); } EIGEN_STRONG_INLINE int cols() const { return m_storage.cols(); } - EIGEN_STRONG_INLINE int stride(void) const + /** Returns the leading dimension (for matrices) or the increment (for vectors) to be used with data(). + * + * More precisely: + * - for a column major matrix it returns the number of elements between two successive columns + * - for a row major matrix it returns the number of elements between two successive rows + * - for a vector it returns the number of elements between two successive coefficients + * This function has to be used together with the MapBase::data() function. + * + * \sa Matrix::data() */ + EIGEN_STRONG_INLINE int stride() const { - if(Flags & RowMajorBit) - return m_storage.cols(); + if(IsVectorAtCompileTime) + return 1; else - return m_storage.rows(); + return (Flags & RowMajorBit) ? m_storage.cols() : m_storage.rows(); } EIGEN_STRONG_INLINE const Scalar& coeff(int row, int col) const diff --git a/Eigen/src/Core/products/SelfadjointMatrixVector.h b/Eigen/src/Core/products/SelfadjointMatrixVector.h index c2c33d5b8..d5927307d 100644 --- a/Eigen/src/Core/products/SelfadjointMatrixVector.h +++ b/Eigen/src/Core/products/SelfadjointMatrixVector.h @@ -185,14 +185,14 @@ struct SelfadjointProductMatrix Scalar actualAlpha = alpha * LhsBlasTraits::extractScalarFactor(m_lhs) * RhsBlasTraits::extractScalarFactor(m_rhs); - ei_assert((&dst.coeff(1))-(&dst.coeff(0))==1 && "not implemented yet"); + ei_assert(dst.stride()==1 && "not implemented yet"); ei_product_selfadjoint_vector::Flags&RowMajorBit, int(LhsUpLo), bool(LhsBlasTraits::NeedToConjugate), bool(RhsBlasTraits::NeedToConjugate)> ( - lhs.rows(), // size - &lhs.coeff(0,0), lhs.stride(), // lhs info - &rhs.coeff(0), (&rhs.coeff(1))-(&rhs.coeff(0)), // rhs info - &dst.coeffRef(0), // result info - actualAlpha // scale factor + lhs.rows(), // size + &lhs.coeff(0,0), lhs.stride(), // lhs info + &rhs.coeff(0), rhs.stride(), // rhs info + &dst.coeffRef(0), // result info + actualAlpha // scale factor ); } }; diff --git a/test/submatrices.cpp b/test/submatrices.cpp index a819cadc2..6fe86c281 100644 --- a/test/submatrices.cpp +++ b/test/submatrices.cpp @@ -170,6 +170,48 @@ template void submatrices(const MatrixType& m) VERIFY(ei_real(ones.row(r1).dot(ones.row(r2))) == RealScalar(cols)); } + +template +void compare_using_data_and_stride(const MatrixType& m) +{ + int rows = m.rows(); + int cols = m.cols(); + int size = m.size(); + int stride = m.stride(); + const typename MatrixType::Scalar* data = m.data(); + + for(int j=0;j +void data_and_stride(const MatrixType& m) +{ + int rows = m.rows(); + int cols = m.cols(); + + int r1 = ei_random(0,rows-1); + int r2 = ei_random(r1,rows-1); + int c1 = ei_random(0,cols-1); + int c2 = ei_random(c1,cols-1); + + MatrixType m1 = MatrixType::Random(rows, cols); + compare_using_data_and_stride(m1.block(r1, c1, r2-r1+1, c2-c1+1)); + compare_using_data_and_stride(m1.transpose().block(c1, r1, c2-c1+1, r2-r1+1)); + compare_using_data_and_stride(m1.row(r1)); + compare_using_data_and_stride(m1.col(c1)); + compare_using_data_and_stride(m1.row(r1).transpose()); + compare_using_data_and_stride(m1.col(c1).transpose()); +} + void test_submatrices() { for(int i = 0; i < g_repeat; i++) { @@ -179,5 +221,8 @@ void test_submatrices() CALL_SUBTEST( submatrices(MatrixXi(8, 12)) ); CALL_SUBTEST( submatrices(MatrixXcd(20, 20)) ); CALL_SUBTEST( submatrices(MatrixXf(20, 20)) ); + + CALL_SUBTEST( data_and_stride(MatrixXf(ei_random(5,50), ei_random(5,50))) ); + CALL_SUBTEST( data_and_stride(Matrix(ei_random(5,50), ei_random(5,50))) ); } } -- cgit v1.2.3 From 5339db61645daa2c829898cbd2c3310b5f80f7a8 Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Mon, 31 Aug 2009 22:08:43 -0400 Subject: add VERIFY_IS_UNITARY --- test/main.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/main.h b/test/main.h index e3866c68b..8c84e525c 100644 --- a/test/main.h +++ b/test/main.h @@ -153,6 +153,8 @@ namespace Eigen #define VERIFY_IS_APPROX_OR_LESS_THAN(a, b) VERIFY(test_ei_isApproxOrLessThan(a, b)) #define VERIFY_IS_NOT_APPROX_OR_LESS_THAN(a, b) VERIFY(!test_ei_isApproxOrLessThan(a, b)) +#define VERIFY_IS_UNITARY(a) VERIFY(test_isUnitary(a)) + #define CALL_SUBTEST(FUNC) do { \ g_test_stack.push_back(EI_PP_MAKE_STRING(FUNC)); \ FUNC; \ @@ -227,6 +229,12 @@ inline bool test_ei_isMuchSmallerThan(const MatrixBase& m, return m.isMuchSmallerThan(s, test_precision::Scalar>()); } +template +inline bool test_isUnitary(const MatrixBase& m) +{ + return m.isUnitary(test_precision::Scalar>()); +} + template void createRandomMatrixOfRank(int desired_rank, int rows, int cols, MatrixType& m) { -- cgit v1.2.3 From 29c6b2452dbe82cd49aa701921f2fa5a20017cc0 Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Mon, 31 Aug 2009 22:09:44 -0400 Subject: simplifications --- test/svd.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/test/svd.cpp b/test/svd.cpp index 2ccd94764..e6a32bd3f 100644 --- a/test/svd.cpp +++ b/test/svd.cpp @@ -41,15 +41,11 @@ template void svd(const MatrixType& m) Matrix::Random(rows,1); Matrix x(cols,1), x2(cols,1); - RealScalar largerEps = test_precision(); - if (ei_is_same_type::ret) - largerEps = 1e-3f; - { SVD svd(a); MatrixType sigma = MatrixType::Zero(rows,cols); MatrixType matU = MatrixType::Zero(rows,rows); - sigma.block(0,0,cols,cols) = svd.singularValues().asDiagonal(); + sigma.diagonal() = svd.singularValues(); matU = svd.matrixU(); VERIFY_IS_APPROX(a, matU * sigma * svd.matrixV().transpose()); } -- cgit v1.2.3 From 6e4e94ff3266f8d85adbfe6556e612a2ff9a7e68 Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Mon, 31 Aug 2009 22:26:15 -0400 Subject: * JacobiSVD: - support complex numbers - big rewrite of the 2x2 kernel, much more robust * Jacobi: - fix weirdness in initial design, e.g. applyJacobiOnTheRight actually did the inverse transformation - fully support complex numbers - fix logic to decide whether to vectorize - remove several clumsy methods fix for complex numbers --- Eigen/SVD | 2 +- Eigen/src/Core/MatrixBase.h | 8 +- Eigen/src/Core/util/ForwardDeclarations.h | 1 + Eigen/src/Jacobi/Jacobi.h | 113 ++++++------- Eigen/src/SVD/JacobiSVD.h | 258 ++++++++++++++++++++++++++++++ Eigen/src/SVD/JacobiSquareSVD.h | 169 ------------------- test/CMakeLists.txt | 1 + test/jacobisvd.cpp | 105 ++++++++++++ 8 files changed, 416 insertions(+), 241 deletions(-) create mode 100644 Eigen/src/SVD/JacobiSVD.h delete mode 100644 Eigen/src/SVD/JacobiSquareSVD.h create mode 100644 test/jacobisvd.cpp diff --git a/Eigen/SVD b/Eigen/SVD index 625071a75..01582310c 100644 --- a/Eigen/SVD +++ b/Eigen/SVD @@ -23,7 +23,7 @@ namespace Eigen { */ #include "src/SVD/SVD.h" -#include "src/SVD/JacobiSquareSVD.h" +#include "src/SVD/JacobiSVD.h" } // namespace Eigen diff --git a/Eigen/src/Core/MatrixBase.h b/Eigen/src/Core/MatrixBase.h index 7cb8853e6..9b1bf9e19 100644 --- a/Eigen/src/Core/MatrixBase.h +++ b/Eigen/src/Core/MatrixBase.h @@ -803,11 +803,11 @@ template class MatrixBase ///////// Jacobi module ///////// - void applyJacobiOnTheLeft(int p, int q, Scalar c, Scalar s); - void applyJacobiOnTheRight(int p, int q, Scalar c, Scalar s); + template + void applyJacobiOnTheLeft(int p, int q, JacobiScalar c, JacobiScalar s); + template + void applyJacobiOnTheRight(int p, int q, JacobiScalar c, JacobiScalar s); bool makeJacobi(int p, int q, Scalar *c, Scalar *s) const; - bool makeJacobiForAtA(int p, int q, Scalar *c, Scalar *s) const; - bool makeJacobiForAAt(int p, int q, Scalar *c, Scalar *s) const; #ifdef EIGEN_MATRIXBASE_PLUGIN #include EIGEN_MATRIXBASE_PLUGIN diff --git a/Eigen/src/Core/util/ForwardDeclarations.h b/Eigen/src/Core/util/ForwardDeclarations.h index 414aaceca..d7dc61e73 100644 --- a/Eigen/src/Core/util/ForwardDeclarations.h +++ b/Eigen/src/Core/util/ForwardDeclarations.h @@ -120,6 +120,7 @@ template class HouseholderQR; template class ColPivotingHouseholderQR; template class FullPivotingHouseholderQR; template class SVD; +template class JacobiSVD; template class LLT; template class LDLT; diff --git a/Eigen/src/Jacobi/Jacobi.h b/Eigen/src/Jacobi/Jacobi.h index 76f0800fe..96f08d54a 100644 --- a/Eigen/src/Jacobi/Jacobi.h +++ b/Eigen/src/Jacobi/Jacobi.h @@ -33,19 +33,20 @@ * * \sa MatrixBase::applyJacobiOnTheLeft(), MatrixBase::applyJacobiOnTheRight() */ -template -void ei_apply_rotation_in_the_plane(VectorX& _x, VectorY& _y, typename VectorX::Scalar c, typename VectorY::Scalar s); +template +void ei_apply_rotation_in_the_plane(VectorX& _x, VectorY& _y, JacobiScalar c, JacobiScalar s); /** Applies a rotation in the plane defined by \a c, \a s to the rows \a p and \a q of \c *this. * More precisely, it computes B = J' * B, with J = [c s ; -s' c] and B = [ *this.row(p) ; *this.row(q) ] * \sa MatrixBase::applyJacobiOnTheRight(), ei_apply_rotation_in_the_plane() */ template -inline void MatrixBase::applyJacobiOnTheLeft(int p, int q, Scalar c, Scalar s) +template +inline void MatrixBase::applyJacobiOnTheLeft(int p, int q, JacobiScalar c, JacobiScalar s) { RowXpr x(row(p)); RowXpr y(row(q)); - ei_apply_rotation_in_the_plane(x, y, ei_conj(c), ei_conj(s)); + ei_apply_rotation_in_the_plane(x, y, c, s); } /** Applies a rotation in the plane defined by \a c, \a s to the columns \a p and \a q of \c *this. @@ -53,23 +54,25 @@ inline void MatrixBase::applyJacobiOnTheLeft(int p, int q, Scalar c, Sc * \sa MatrixBase::applyJacobiOnTheLeft(), ei_apply_rotation_in_the_plane() */ template -inline void MatrixBase::applyJacobiOnTheRight(int p, int q, Scalar c, Scalar s) +template +inline void MatrixBase::applyJacobiOnTheRight(int p, int q, JacobiScalar c, JacobiScalar s) { ColXpr x(col(p)); ColXpr y(col(q)); - ei_apply_rotation_in_the_plane(x, y, c, s); + ei_apply_rotation_in_the_plane(x, y, c, -ei_conj(s)); } /** Computes the cosine-sine pair (\a c, \a s) such that its associated - * rotation \f$ J = ( \begin{array}{cc} c & s \\ -s' c \end{array} )\f$ + * rotation \f$ J = ( \begin{array}{cc} c & \overline s \\ -s & \overline c \end{array} )\f$ * applied to both the right and left of the 2x2 matrix * \f$ B = ( \begin{array}{cc} x & y \\ * & z \end{array} )\f$ yields - * a diagonal matrix A: \f$ A = J' B J \f$ + * a diagonal matrix A: \f$ A = J^* B J \f$ */ template -bool ei_makeJacobi(Scalar x, Scalar y, Scalar z, Scalar *c, Scalar *s) +bool ei_makeJacobi(typename NumTraits::Real x, Scalar y, typename NumTraits::Real z, Scalar *c, Scalar *s) { - if(y == 0) + typedef typename NumTraits::Real RealScalar; + if(y == Scalar(0)) { *c = Scalar(1); *s = Scalar(0); @@ -77,15 +80,21 @@ bool ei_makeJacobi(Scalar x, Scalar y, Scalar z, Scalar *c, Scalar *s) } else { - Scalar tau = (z - x) / (2 * y); - Scalar w = ei_sqrt(1 + ei_abs2(tau)); - Scalar t; + RealScalar tau = (x-z)/(RealScalar(2)*ei_abs(y)); + RealScalar w = ei_sqrt(ei_abs2(tau) + 1); + RealScalar t; if(tau>0) - t = Scalar(1) / (tau + w); + { + t = RealScalar(1) / (tau + w); + } else - t = Scalar(1) / (tau - w); - *c = Scalar(1) / ei_sqrt(1 + ei_abs2(t)); - *s = *c * t; + { + t = RealScalar(1) / (tau - w); + } + RealScalar sign_t = t > 0 ? 1 : -1; + RealScalar n = RealScalar(1) / ei_sqrt(ei_abs2(t)+1); + *s = - sign_t * (ei_conj(y) / ei_abs(y)) * ei_abs(t) * n; + *c = n; return true; } } @@ -93,41 +102,11 @@ bool ei_makeJacobi(Scalar x, Scalar y, Scalar z, Scalar *c, Scalar *s) template inline bool MatrixBase::makeJacobi(int p, int q, Scalar *c, Scalar *s) const { - return ei_makeJacobi(coeff(p,p), coeff(p,q), coeff(q,q), c, s); -} - -template -inline bool MatrixBase::makeJacobiForAtA(int p, int q, Scalar *c, Scalar *s) const -{ - return ei_makeJacobi(ei_abs2(coeff(p,p)) + ei_abs2(coeff(q,p)), - ei_conj(coeff(p,p))*coeff(p,q) + ei_conj(coeff(q,p))*coeff(q,q), - ei_abs2(coeff(p,q)) + ei_abs2(coeff(q,q)), - c,s); -} - -template -inline bool MatrixBase::makeJacobiForAAt(int p, int q, Scalar *c, Scalar *s) const -{ - return ei_makeJacobi(ei_abs2(coeff(p,p)) + ei_abs2(coeff(p,q)), - ei_conj(coeff(q,p))*coeff(p,p) + ei_conj(coeff(q,q))*coeff(p,q), - ei_abs2(coeff(q,p)) + ei_abs2(coeff(q,q)), - c,s); -} - -template -inline void ei_normalizeJacobi(Scalar *c, Scalar *s, const Scalar& x, const Scalar& y) -{ - Scalar a = x * *c - y * *s; - Scalar b = x * *s + y * *c; - if(ei_abs(b)>ei_abs(a)) { - Scalar x = *c; - *c = -*s; - *s = x; - } + return ei_makeJacobi(ei_real(coeff(p,p)), coeff(p,q), ei_real(coeff(q,q)), c, s); } -template -void /*EIGEN_DONT_INLINE*/ ei_apply_rotation_in_the_plane(VectorX& _x, VectorY& _y, typename VectorX::Scalar c, typename VectorY::Scalar s) +template +void /*EIGEN_DONT_INLINE*/ ei_apply_rotation_in_the_plane(VectorX& _x, VectorY& _y, JacobiScalar c, JacobiScalar s) { typedef typename VectorX::Scalar Scalar; ei_assert(_x.size() == _y.size()); @@ -138,7 +117,7 @@ void /*EIGEN_DONT_INLINE*/ ei_apply_rotation_in_the_plane(VectorX& _x, VectorY& Scalar* EIGEN_RESTRICT x = &_x.coeffRef(0); Scalar* EIGEN_RESTRICT y = &_y.coeffRef(0); - if (incrx==1 && incry==1) + if((VectorX::Flags & VectorY::Flags & PacketAccessBit) && incrx==1 && incry==1) { // both vectors are sequentially stored in memory => vectorization typedef typename ei_packet_traits::type Packet; @@ -147,16 +126,16 @@ void /*EIGEN_DONT_INLINE*/ ei_apply_rotation_in_the_plane(VectorX& _x, VectorY& int alignedStart = ei_alignmentOffset(y, size); int alignedEnd = alignedStart + ((size-alignedStart)/PacketSize)*PacketSize; - const Packet pc = ei_pset1(c); - const Packet ps = ei_pset1(s); + const Packet pc = ei_pset1(Scalar(c)); + const Packet ps = ei_pset1(Scalar(s)); ei_conj_helper::IsComplex,false> cj; for(int i=0; i +// +// 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 . + +#ifndef EIGEN_JACOBISVD_H +#define EIGEN_JACOBISVD_H + +/** \ingroup SVD_Module + * \nonstableyet + * + * \class JacobiSVD + * + * \brief Jacobi SVD decomposition of a square matrix + * + * \param MatrixType the type of the matrix of which we are computing the SVD decomposition + * \param ComputeU whether the U matrix should be computed + * \param ComputeV whether the V matrix should be computed + * + * \sa MatrixBase::jacobiSvd() + */ +template class JacobiSVD +{ + private: + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits::Real RealScalar; + enum { + ComputeU = 1, + ComputeV = 1, + RowsAtCompileTime = MatrixType::RowsAtCompileTime, + ColsAtCompileTime = MatrixType::ColsAtCompileTime, + DiagSizeAtCompileTime = EIGEN_ENUM_MIN(RowsAtCompileTime,ColsAtCompileTime), + MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime, + MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime, + MaxDiagSizeAtCompileTime = EIGEN_ENUM_MIN(MaxRowsAtCompileTime,MaxColsAtCompileTime), + MatrixOptions = MatrixType::Options + }; + + typedef Matrix DummyMatrixType; + typedef typename ei_meta_if, + DummyMatrixType>::ret MatrixUType; + typedef typename ei_meta_if, + DummyMatrixType>::ret MatrixVType; + typedef Matrix SingularValuesType; + typedef Matrix RowType; + typedef Matrix ColType; + + public: + + JacobiSVD() : m_isInitialized(false) {} + + JacobiSVD(const MatrixType& matrix) : m_isInitialized(false) + { + compute(matrix); + } + + JacobiSVD& compute(const MatrixType& matrix); + + const MatrixUType& matrixU() const + { + ei_assert(m_isInitialized && "JacobiSVD is not initialized."); + return m_matrixU; + } + + const SingularValuesType& singularValues() const + { + ei_assert(m_isInitialized && "JacobiSVD is not initialized."); + return m_singularValues; + } + + const MatrixUType& matrixV() const + { + ei_assert(m_isInitialized && "JacobiSVD is not initialized."); + return m_matrixV; + } + + protected: + MatrixUType m_matrixU; + MatrixVType m_matrixV; + SingularValuesType m_singularValues; + bool m_isInitialized; + + template + friend struct ei_svd_precondition_2x2_block_to_be_real; +}; + +template::IsComplex> +struct ei_svd_precondition_2x2_block_to_be_real +{ + static void run(MatrixType&, JacobiSVD&, int, int) {} +}; + +template +struct ei_svd_precondition_2x2_block_to_be_real +{ + typedef JacobiSVD SVD; + typedef typename MatrixType::Scalar Scalar; + typedef typename MatrixType::RealScalar RealScalar; + + enum { ComputeU = SVD::ComputeU, ComputeV = SVD::ComputeV }; + static void run(MatrixType& work_matrix, JacobiSVD& svd, int p, int q) + { + Scalar c, s, z; + RealScalar n = ei_sqrt(ei_abs2(work_matrix.coeff(p,p)) + ei_abs2(work_matrix.coeff(q,p))); + if(n==0) + { + z = ei_abs(work_matrix.coeff(p,q)) / work_matrix.coeff(p,q); + work_matrix.row(p) *= z; + if(ComputeU) svd.m_matrixU.col(p) *= ei_conj(z); + z = ei_abs(work_matrix.coeff(q,q)) / work_matrix.coeff(q,q); + work_matrix.row(q) *= z; + if(ComputeU) svd.m_matrixU.col(q) *= ei_conj(z); + } + else + { + c = ei_conj(work_matrix.coeff(p,p)) / n; + s = work_matrix.coeff(q,p) / n; + work_matrix.applyJacobiOnTheLeft(p,q,c,s); + if(ComputeU) svd.m_matrixU.applyJacobiOnTheRight(p,q,ei_conj(c),-s); + if(work_matrix.coeff(p,q) != Scalar(0)) + { + Scalar z = ei_abs(work_matrix.coeff(p,q)) / work_matrix.coeff(p,q); + work_matrix.col(q) *= z; + if(ComputeV) svd.m_matrixV.col(q) *= z; + } + if(work_matrix.coeff(q,q) != Scalar(0)) + { + z = ei_abs(work_matrix.coeff(q,q)) / work_matrix.coeff(q,q); + work_matrix.row(q) *= z; + if(ComputeU) svd.m_matrixU.col(q) *= ei_conj(z); + } + } + } +}; + +template +void ei_real_2x2_jacobi_svd(const MatrixType& matrix, int p, int q, + RealScalar *c_left, RealScalar *s_left, + RealScalar *c_right, RealScalar *s_right) +{ + Matrix m; + m << ei_real(matrix.coeff(p,p)), ei_real(matrix.coeff(p,q)), + ei_real(matrix.coeff(q,p)), ei_real(matrix.coeff(q,q)); + RealScalar c1, s1; + RealScalar t = m.coeff(0,0) + m.coeff(1,1); + RealScalar d = m.coeff(1,0) - m.coeff(0,1); + if(t == RealScalar(0)) + { + c1 = 0; + s1 = d > 0 ? 1 : -1; + } + else + { + RealScalar u = d / t; + c1 = RealScalar(1) / ei_sqrt(1 + ei_abs2(u)); + s1 = c1 * u; + } + m.applyJacobiOnTheLeft(0,1,c1,s1); + RealScalar c2, s2; + m.makeJacobi(0,1,&c2,&s2); + *c_left = c1*c2 + s1*s2; + *s_left = s1*c2 - c1*s2; + *c_right = c2; + *s_right = s2; +} + +template +JacobiSVD& JacobiSVD::compute(const MatrixType& matrix) +{ + MatrixType work_matrix(matrix); + int size = matrix.rows(); + if(ComputeU) m_matrixU = MatrixUType::Identity(size,size); + if(ComputeV) m_matrixV = MatrixUType::Identity(size,size); + m_singularValues.resize(size); + const RealScalar precision = 2 * epsilon(); + +sweep_again: + for(int p = 1; p < size; ++p) + { + for(int q = 0; q < p; ++q) + { + if(std::max(ei_abs(work_matrix.coeff(p,q)),ei_abs(work_matrix.coeff(q,p))) + > std::max(ei_abs(work_matrix.coeff(p,p)),ei_abs(work_matrix.coeff(q,q)))*precision) + { + ei_svd_precondition_2x2_block_to_be_real::run(work_matrix, *this, p, q); + + RealScalar c_left, s_left, c_right, s_right; + ei_real_2x2_jacobi_svd(work_matrix, p, q, &c_left, &s_left, &c_right, &s_right); + + work_matrix.applyJacobiOnTheLeft(p,q,c_left,s_left); + if(ComputeU) m_matrixU.applyJacobiOnTheRight(p,q,c_left,-s_left); + + work_matrix.applyJacobiOnTheRight(p,q,c_right,s_right); + if(ComputeV) m_matrixV.applyJacobiOnTheRight(p,q,c_right,s_right); + } + } + } + + RealScalar biggestOnDiag = work_matrix.diagonal().cwise().abs().maxCoeff(); + RealScalar maxAllowedOffDiag = biggestOnDiag * precision; + for(int p = 0; p < size; ++p) + { + for(int q = 0; q < p; ++q) + if(ei_abs(work_matrix.coeff(p,q)) > maxAllowedOffDiag) + goto sweep_again; + for(int q = p+1; q < size; ++q) + if(ei_abs(work_matrix.coeff(p,q)) > maxAllowedOffDiag) + goto sweep_again; + } + + for(int i = 0; i < size; ++i) + { + RealScalar a = ei_abs(work_matrix.coeff(i,i)); + m_singularValues.coeffRef(i) = a; + if(ComputeU && (a!=RealScalar(0))) m_matrixU.col(i) *= work_matrix.coeff(i,i)/a; + } + + for(int i = 0; i < size; i++) + { + int pos; + m_singularValues.end(size-i).maxCoeff(&pos); + if(pos) + { + pos += i; + std::swap(m_singularValues.coeffRef(i), m_singularValues.coeffRef(pos)); + if(ComputeU) m_matrixU.col(pos).swap(m_matrixU.col(i)); + if(ComputeV) m_matrixV.col(pos).swap(m_matrixV.col(i)); + } + } + + m_isInitialized = true; + return *this; +} +#endif // EIGEN_JACOBISVD_H diff --git a/Eigen/src/SVD/JacobiSquareSVD.h b/Eigen/src/SVD/JacobiSquareSVD.h deleted file mode 100644 index f21c9edca..000000000 --- a/Eigen/src/SVD/JacobiSquareSVD.h +++ /dev/null @@ -1,169 +0,0 @@ -// This file is part of Eigen, a lightweight C++ template library -// for linear algebra. -// -// Copyright (C) 2009 Benoit Jacob -// -// 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 . - -#ifndef EIGEN_JACOBISQUARESVD_H -#define EIGEN_JACOBISQUARESVD_H - -/** \ingroup SVD_Module - * \nonstableyet - * - * \class JacobiSquareSVD - * - * \brief Jacobi SVD decomposition of a square matrix - * - * \param MatrixType the type of the matrix of which we are computing the SVD decomposition - * \param ComputeU whether the U matrix should be computed - * \param ComputeV whether the V matrix should be computed - * - * \sa MatrixBase::jacobiSvd() - */ -template class JacobiSquareSVD -{ - private: - typedef typename MatrixType::Scalar Scalar; - typedef typename NumTraits::Real RealScalar; - enum { - RowsAtCompileTime = MatrixType::RowsAtCompileTime, - MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime, - Options = MatrixType::Options - }; - - typedef Matrix DummyMatrixType; - typedef typename ei_meta_if, - DummyMatrixType>::ret MatrixUType; - typedef typename Diagonal::PlainMatrixType SingularValuesType; - typedef Matrix RowType; - typedef Matrix ColType; - - public: - - JacobiSquareSVD() : m_isInitialized(false) {} - - JacobiSquareSVD(const MatrixType& matrix) : m_isInitialized(false) - { - compute(matrix); - } - - JacobiSquareSVD& compute(const MatrixType& matrix); - - const MatrixUType& matrixU() const - { - ei_assert(m_isInitialized && "SVD is not initialized."); - return m_matrixU; - } - - const SingularValuesType& singularValues() const - { - ei_assert(m_isInitialized && "SVD is not initialized."); - return m_singularValues; - } - - const MatrixUType& matrixV() const - { - ei_assert(m_isInitialized && "SVD is not initialized."); - return m_matrixV; - } - - protected: - MatrixUType m_matrixU; - MatrixUType m_matrixV; - SingularValuesType m_singularValues; - bool m_isInitialized; -}; - -template -JacobiSquareSVD& JacobiSquareSVD::compute(const MatrixType& matrix) -{ - MatrixType work_matrix(matrix); - int size = matrix.rows(); - if(ComputeU) m_matrixU = MatrixUType::Identity(size,size); - if(ComputeV) m_matrixV = MatrixUType::Identity(size,size); - m_singularValues.resize(size); - const RealScalar precision = 2 * epsilon(); - -sweep_again: - for(int p = 1; p < size; ++p) - { - for(int q = 0; q < p; ++q) - { - Scalar c, s; - while(std::max(ei_abs(work_matrix.coeff(p,q)),ei_abs(work_matrix.coeff(q,p))) - > std::max(ei_abs(work_matrix.coeff(p,p)),ei_abs(work_matrix.coeff(q,q)))*precision) - { - if(work_matrix.makeJacobiForAtA(p,q,&c,&s)) - { - work_matrix.applyJacobiOnTheRight(p,q,c,s); - if(ComputeV) m_matrixV.applyJacobiOnTheRight(p,q,c,s); - } - if(work_matrix.makeJacobiForAAt(p,q,&c,&s)) - { - ei_normalizeJacobi(&c, &s, work_matrix.coeff(p,p), work_matrix.coeff(q,p)), - work_matrix.applyJacobiOnTheLeft(p,q,c,s); - if(ComputeU) m_matrixU.applyJacobiOnTheRight(p,q,c,s); - } - } - } - } - - RealScalar biggestOnDiag = work_matrix.diagonal().cwise().abs().maxCoeff(); - RealScalar maxAllowedOffDiag = biggestOnDiag * precision; - for(int p = 0; p < size; ++p) - { - for(int q = 0; q < p; ++q) - if(ei_abs(work_matrix.coeff(p,q)) > maxAllowedOffDiag) - goto sweep_again; - for(int q = p+1; q < size; ++q) - if(ei_abs(work_matrix.coeff(p,q)) > maxAllowedOffDiag) - goto sweep_again; - } - - m_singularValues = work_matrix.diagonal().cwise().abs(); - RealScalar biggestSingularValue = m_singularValues.maxCoeff(); - - for(int i = 0; i < size; ++i) - { - RealScalar a = ei_abs(work_matrix.coeff(i,i)); - m_singularValues.coeffRef(i) = a; - if(ComputeU && !ei_isMuchSmallerThan(a, biggestSingularValue)) m_matrixU.col(i) *= work_matrix.coeff(i,i)/a; - } - - for(int i = 0; i < size; i++) - { - int pos; - m_singularValues.end(size-i).maxCoeff(&pos); - if(pos) - { - pos += i; - std::swap(m_singularValues.coeffRef(i), m_singularValues.coeffRef(pos)); - if(ComputeU) m_matrixU.col(pos).swap(m_matrixU.col(i)); - if(ComputeV) m_matrixV.col(pos).swap(m_matrixV.col(i)); - } - } - - m_isInitialized = true; - return *this; -} -#endif // EIGEN_JACOBISQUARESVD_H diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index fc43bbb1d..896392188 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -126,6 +126,7 @@ ei_add_test(qr_fullpivoting) ei_add_test(eigensolver_selfadjoint " " "${GSL_LIBRARIES}") ei_add_test(eigensolver_generic " " "${GSL_LIBRARIES}") ei_add_test(svd) +ei_add_test(jacobisvd ${EI_OFLAG}) ei_add_test(geo_orthomethods) ei_add_test(geo_homogeneous) ei_add_test(geo_quaternion) diff --git a/test/jacobisvd.cpp b/test/jacobisvd.cpp new file mode 100644 index 000000000..9a4d79e45 --- /dev/null +++ b/test/jacobisvd.cpp @@ -0,0 +1,105 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008 Gael Guennebaud +// Copyright (C) 2009 Benoit Jacob +// +// 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 . + +#include "main.h" +#include +#include + +template void svd(const MatrixType& m, bool pickrandom = true) +{ + int rows = m.rows(); + int cols = m.cols(); + + enum { + RowsAtCompileTime = MatrixType::RowsAtCompileTime, + ColsAtCompileTime = MatrixType::ColsAtCompileTime + }; + + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits::Real RealScalar; + typedef Matrix MatrixUType; + typedef Matrix MatrixVType; + typedef Matrix ColVectorType; + typedef Matrix InputVectorType; + + MatrixType a; + if(pickrandom) a = MatrixType::Random(rows,cols); + else a = m; + + JacobiSVD svd(a); + MatrixType sigma = MatrixType::Zero(rows,cols); + sigma.diagonal() = svd.singularValues().template cast(); + MatrixUType u = svd.matrixU(); + MatrixVType v = svd.matrixV(); + + VERIFY_IS_APPROX(a, u * sigma * v.adjoint()); + VERIFY_IS_UNITARY(u); + VERIFY_IS_UNITARY(v); +} + +template void svd_verify_assert() +{ + MatrixType tmp; + + SVD svd; + //VERIFY_RAISES_ASSERT(svd.solve(tmp, &tmp)) + VERIFY_RAISES_ASSERT(svd.matrixU()) + VERIFY_RAISES_ASSERT(svd.singularValues()) + VERIFY_RAISES_ASSERT(svd.matrixV()) + /*VERIFY_RAISES_ASSERT(svd.computeUnitaryPositive(&tmp,&tmp)) + VERIFY_RAISES_ASSERT(svd.computePositiveUnitary(&tmp,&tmp)) + VERIFY_RAISES_ASSERT(svd.computeRotationScaling(&tmp,&tmp)) + VERIFY_RAISES_ASSERT(svd.computeScalingRotation(&tmp,&tmp))*/ +} + +void test_jacobisvd() +{ + for(int i = 0; i < g_repeat; i++) { + Matrix2cd m; + m << 0, 1, + 0, 1; + CALL_SUBTEST( svd(m, false) ); + m << 1, 0, + 1, 0; + CALL_SUBTEST( svd(m, false) ); + Matrix2d n; + n << 1, 1, + 1, -1; + CALL_SUBTEST( svd(n, false) ); + CALL_SUBTEST( svd(Matrix3f()) ); + CALL_SUBTEST( svd(Matrix4d()) ); + CALL_SUBTEST( svd(MatrixXf(50,50)) ); +// CALL_SUBTEST( svd(MatrixXd(14,7)) ); + CALL_SUBTEST( svd(MatrixXcf(3,3)) ); + CALL_SUBTEST( svd(MatrixXd(30,30)) ); + } + CALL_SUBTEST( svd(MatrixXf(200,200)) ); + CALL_SUBTEST( svd(MatrixXcd(100,100)) ); + + CALL_SUBTEST( svd_verify_assert() ); + CALL_SUBTEST( svd_verify_assert() ); + CALL_SUBTEST( svd_verify_assert() ); + CALL_SUBTEST( svd_verify_assert() ); +} -- cgit v1.2.3 From 32f95ec2670a287234d7f614a20062e7d8499906 Mon Sep 17 00:00:00 2001 From: Jitse Niesen Date: Tue, 1 Sep 2009 10:50:54 +0100 Subject: Bug fix in MatrixExponential.h Initialize matrices for intermediate results to correct dimension --- unsupported/Eigen/src/MatrixFunctions/MatrixExponential.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/unsupported/Eigen/src/MatrixFunctions/MatrixExponential.h b/unsupported/Eigen/src/MatrixFunctions/MatrixExponential.h index 7d8777de7..bf5b79955 100644 --- a/unsupported/Eigen/src/MatrixFunctions/MatrixExponential.h +++ b/unsupported/Eigen/src/MatrixFunctions/MatrixExponential.h @@ -292,7 +292,10 @@ namespace MatrixExponentialInternal { template void compute(const MatrixType &M, MatrixType* result) { - MatrixType num, den, U, V; + MatrixType num(M.rows(), M.cols()); + MatrixType den(M.rows(), M.cols()); + MatrixType U(M.rows(), M.cols()); + MatrixType V(M.rows(), M.cols()); MatrixType Id = MatrixType::Identity(M.rows(), M.cols()); float l1norm = static_cast(M.cwise().abs().colwise().sum().maxCoeff()); int squarings; -- cgit v1.2.3 From 8392373d960c088b076b125775ccfc6a91f7d25e Mon Sep 17 00:00:00 2001 From: Gael Guennebaud Date: Tue, 1 Sep 2009 13:18:03 +0200 Subject: add a JacobiRotation class wrapping the cosine-sine pair with some convenient features (transpose, adjoint, product) --- Eigen/src/Core/MatrixBase.h | 6 +- Eigen/src/Core/util/ForwardDeclarations.h | 1 + Eigen/src/Jacobi/Jacobi.h | 120 +++++++++++++++++++++--------- Eigen/src/SVD/JacobiSVD.h | 73 +++++++++--------- 4 files changed, 123 insertions(+), 77 deletions(-) diff --git a/Eigen/src/Core/MatrixBase.h b/Eigen/src/Core/MatrixBase.h index 9b1bf9e19..c2945ab46 100644 --- a/Eigen/src/Core/MatrixBase.h +++ b/Eigen/src/Core/MatrixBase.h @@ -804,10 +804,10 @@ template class MatrixBase ///////// Jacobi module ///////// template - void applyJacobiOnTheLeft(int p, int q, JacobiScalar c, JacobiScalar s); + void applyJacobiOnTheLeft(int p, int q, const JacobiRotation& j); template - void applyJacobiOnTheRight(int p, int q, JacobiScalar c, JacobiScalar s); - bool makeJacobi(int p, int q, Scalar *c, Scalar *s) const; + void applyJacobiOnTheRight(int p, int q, const JacobiRotation& j); + bool makeJacobi(int p, int q, JacobiRotation *j) const; #ifdef EIGEN_MATRIXBASE_PLUGIN #include EIGEN_MATRIXBASE_PLUGIN diff --git a/Eigen/src/Core/util/ForwardDeclarations.h b/Eigen/src/Core/util/ForwardDeclarations.h index d7dc61e73..18d3af7c5 100644 --- a/Eigen/src/Core/util/ForwardDeclarations.h +++ b/Eigen/src/Core/util/ForwardDeclarations.h @@ -123,6 +123,7 @@ template class SVD; template class JacobiSVD; template class LLT; template class LDLT; +template class JacobiRotation; // Geometry module: template class RotationBase; diff --git a/Eigen/src/Jacobi/Jacobi.h b/Eigen/src/Jacobi/Jacobi.h index 96f08d54a..24fb7e782 100644 --- a/Eigen/src/Jacobi/Jacobi.h +++ b/Eigen/src/Jacobi/Jacobi.h @@ -26,56 +26,98 @@ #ifndef EIGEN_JACOBI_H #define EIGEN_JACOBI_H -/** Applies the counter clock wise 2D rotation of angle \c theta given by its - * cosine \a c and sine \a s to the set of 2D vectors of cordinates \a x and \a y: - * \f$ x = c x - s' y \f$ - * \f$ y = s x + c y \f$ +/** \ingroup Jacobi + * \class JacobiRotation + * \brief Represents a rotation in the plane from a cosine-sine pair. + * + * This class represents a Jacobi rotation which is also known as a Givens rotation. + * This is a 2D clock-wise rotation in the plane \c J of angle \f$ \theta \f$ defined by + * its cosine \c c and sine \c s as follow: + * \f$ J = \left ( \begin{array}{cc} c & \overline s \\ -s & \overline c \end{array} \right ) \f$ + * + * \sa MatrixBase::makeJacobi(), MatrixBase::applyJacobiOnTheLeft(), MatrixBase::applyJacobiOnTheRight() + */ +template class JacobiRotation +{ + public: + /** Default constructor without any initialization. */ + JacobiRotation() {} + + /** Construct a Jacobi rotation from a cosine-sine pair (\a c, \c s). */ + JacobiRotation(const Scalar& c, const Scalar& s) : m_c(c), m_s(s) {} + + Scalar& c() { return m_c; } + Scalar c() const { return m_c; } + Scalar& s() { return m_s; } + Scalar s() const { return m_s; } + + /** Concatenates two Jacobi rotation */ + JacobiRotation operator*(const JacobiRotation& other) + { + return JacobiRotation(m_c * other.m_c - ei_conj(m_s) * other.m_s, + ei_conj(m_c * ei_conj(other.m_s) + ei_conj(m_s) * ei_conj(other.m_c))); + } + + /** Returns the transposed transformation */ + JacobiRotation transpose() const { return JacobiRotation(m_c, -ei_conj(m_s)); } + + /** Returns the adjoint transformation */ + JacobiRotation adjoint() const { return JacobiRotation(ei_conj(m_c), -m_s); } + + protected: + Scalar m_c, m_s; +}; + +/** Applies the clock wise 2D rotation \a j to the set of 2D vectors of cordinates \a x and \a y: + * \f$ \left ( \begin{array}{cc} x \\ y \end{array} \right ) = J \left ( \begin{array}{cc} x \\ y \end{array} \right ) \f$ * * \sa MatrixBase::applyJacobiOnTheLeft(), MatrixBase::applyJacobiOnTheRight() */ template -void ei_apply_rotation_in_the_plane(VectorX& _x, VectorY& _y, JacobiScalar c, JacobiScalar s); +void ei_apply_rotation_in_the_plane(VectorX& _x, VectorY& _y, const JacobiRotation& j); -/** Applies a rotation in the plane defined by \a c, \a s to the rows \a p and \a q of \c *this. - * More precisely, it computes B = J' * B, with J = [c s ; -s' c] and B = [ *this.row(p) ; *this.row(q) ] - * \sa MatrixBase::applyJacobiOnTheRight(), ei_apply_rotation_in_the_plane() +/** Applies the rotation in the plane \a j to the rows \a p and \a q of \c *this, i.e., it computes B = J * B, + * with \f$ B = \left ( \begin{array}{cc} \text{*this.row}(p) \\ \text{*this.row}(q) \end{array} \right ) \f$. + * + * \sa class JacobiRotation, MatrixBase::applyJacobiOnTheRight(), ei_apply_rotation_in_the_plane() */ template template -inline void MatrixBase::applyJacobiOnTheLeft(int p, int q, JacobiScalar c, JacobiScalar s) +inline void MatrixBase::applyJacobiOnTheLeft(int p, int q, const JacobiRotation& j) { RowXpr x(row(p)); RowXpr y(row(q)); - ei_apply_rotation_in_the_plane(x, y, c, s); + ei_apply_rotation_in_the_plane(x, y, j); } -/** Applies a rotation in the plane defined by \a c, \a s to the columns \a p and \a q of \c *this. - * More precisely, it computes B = B * J, with J = [c s ; -s' c] and B = [ *this.col(p) ; *this.col(q) ] - * \sa MatrixBase::applyJacobiOnTheLeft(), ei_apply_rotation_in_the_plane() +/** Applies the rotation in the plane \a j to the columns \a p and \a q of \c *this, i.e., it computes B = B * J + * with \f$ B = \left ( \begin{array}{cc} \text{*this.col}(p) & \text{*this.col}(q) \end{array} \right ) \f$. + * + * \sa class JacobiRotation, MatrixBase::applyJacobiOnTheLeft(), ei_apply_rotation_in_the_plane() */ template template -inline void MatrixBase::applyJacobiOnTheRight(int p, int q, JacobiScalar c, JacobiScalar s) +inline void MatrixBase::applyJacobiOnTheRight(int p, int q, const JacobiRotation& j) { ColXpr x(col(p)); ColXpr y(col(q)); - ei_apply_rotation_in_the_plane(x, y, c, -ei_conj(s)); + ei_apply_rotation_in_the_plane(x, y, j.transpose()); } -/** Computes the cosine-sine pair (\a c, \a s) such that its associated - * rotation \f$ J = ( \begin{array}{cc} c & \overline s \\ -s & \overline c \end{array} )\f$ - * applied to both the right and left of the 2x2 matrix - * \f$ B = ( \begin{array}{cc} x & y \\ * & z \end{array} )\f$ yields - * a diagonal matrix A: \f$ A = J^* B J \f$ +/** Computes the Jacobi rotation \a J such that applying \a J on both the right and left sides of the 2x2 matrix + * \f$ B = \left ( \begin{array}{cc} x & y \\ * & z \end{array} \right )\f$ yields + * a diagonal matrix \f$ A = J^* B J \f$ + * + * \sa MatrixBase::makeJacobi(), MatrixBase::applyJacobiOnTheLeft(), MatrixBase::applyJacobiOnTheRight() */ template -bool ei_makeJacobi(typename NumTraits::Real x, Scalar y, typename NumTraits::Real z, Scalar *c, Scalar *s) +bool ei_makeJacobi(typename NumTraits::Real x, Scalar y, typename NumTraits::Real z, JacobiRotation *j) { typedef typename NumTraits::Real RealScalar; if(y == Scalar(0)) { - *c = Scalar(1); - *s = Scalar(0); + j->c() = Scalar(1); + j->s() = Scalar(0); return false; } else @@ -93,20 +135,26 @@ bool ei_makeJacobi(typename NumTraits::Real x, Scalar y, typename NumTra } RealScalar sign_t = t > 0 ? 1 : -1; RealScalar n = RealScalar(1) / ei_sqrt(ei_abs2(t)+1); - *s = - sign_t * (ei_conj(y) / ei_abs(y)) * ei_abs(t) * n; - *c = n; + j->s() = - sign_t * (ei_conj(y) / ei_abs(y)) * ei_abs(t) * n; + j->c() = n; return true; } } +/** Computes the Jacobi rotation \a J such that applying \a J on both the right and left sides of the 2x2 matrix + * \f$ B = \left ( \begin{array}{cc} \text{this}_{pp} & \text{this}_{pq} \\ * & \text{this}_{qq} \end{array} \right )\f$ yields + * a diagonal matrix \f$ A = J^* B J \f$ + * + * \sa MatrixBase::ei_make_jacobi(), MatrixBase::applyJacobiOnTheLeft(), MatrixBase::applyJacobiOnTheRight() + */ template -inline bool MatrixBase::makeJacobi(int p, int q, Scalar *c, Scalar *s) const +inline bool MatrixBase::makeJacobi(int p, int q, JacobiRotation *j) const { - return ei_makeJacobi(ei_real(coeff(p,p)), coeff(p,q), ei_real(coeff(q,q)), c, s); + return ei_makeJacobi(ei_real(coeff(p,p)), coeff(p,q), ei_real(coeff(q,q)), j); } template -void /*EIGEN_DONT_INLINE*/ ei_apply_rotation_in_the_plane(VectorX& _x, VectorY& _y, JacobiScalar c, JacobiScalar s) +void /*EIGEN_DONT_INLINE*/ ei_apply_rotation_in_the_plane(VectorX& _x, VectorY& _y, const JacobiRotation& j) { typedef typename VectorX::Scalar Scalar; ei_assert(_x.size() == _y.size()); @@ -126,16 +174,16 @@ void /*EIGEN_DONT_INLINE*/ ei_apply_rotation_in_the_plane(VectorX& _x, VectorY& int alignedStart = ei_alignmentOffset(y, size); int alignedEnd = alignedStart + ((size-alignedStart)/PacketSize)*PacketSize; - const Packet pc = ei_pset1(Scalar(c)); - const Packet ps = ei_pset1(Scalar(s)); + const Packet pc = ei_pset1(Scalar(j.c())); + const Packet ps = ei_pset1(Scalar(j.s())); ei_conj_helper::IsComplex,false> cj; for(int i=0; i class JacobiSVD MaxDiagSizeAtCompileTime = EIGEN_ENUM_MIN(MaxRowsAtCompileTime,MaxColsAtCompileTime), MatrixOptions = MatrixType::Options }; - + typedef Matrix DummyMatrixType; typedef typename ei_meta_if class JacobiSVD JacobiSVD() : m_isInitialized(false) {} - JacobiSVD(const MatrixType& matrix) : m_isInitialized(false) + JacobiSVD(const MatrixType& matrix) : m_isInitialized(false) { compute(matrix); } - + JacobiSVD& compute(const MatrixType& matrix); - + const MatrixUType& matrixU() const { ei_assert(m_isInitialized && "JacobiSVD is not initialized."); @@ -103,7 +103,7 @@ template class JacobiSVD MatrixVType m_matrixV; SingularValuesType m_singularValues; bool m_isInitialized; - + template friend struct ei_svd_precondition_2x2_block_to_be_real; }; @@ -120,11 +120,12 @@ struct ei_svd_precondition_2x2_block_to_be_real typedef JacobiSVD SVD; typedef typename MatrixType::Scalar Scalar; typedef typename MatrixType::RealScalar RealScalar; - + enum { ComputeU = SVD::ComputeU, ComputeV = SVD::ComputeV }; static void run(MatrixType& work_matrix, JacobiSVD& svd, int p, int q) { - Scalar c, s, z; + Scalar z; + JacobiRotation rot; RealScalar n = ei_sqrt(ei_abs2(work_matrix.coeff(p,p)) + ei_abs2(work_matrix.coeff(q,p))); if(n==0) { @@ -137,10 +138,10 @@ struct ei_svd_precondition_2x2_block_to_be_real } else { - c = ei_conj(work_matrix.coeff(p,p)) / n; - s = work_matrix.coeff(q,p) / n; - work_matrix.applyJacobiOnTheLeft(p,q,c,s); - if(ComputeU) svd.m_matrixU.applyJacobiOnTheRight(p,q,ei_conj(c),-s); + rot.c() = ei_conj(work_matrix.coeff(p,p)) / n; + rot.s() = work_matrix.coeff(q,p) / n; + work_matrix.applyJacobiOnTheLeft(p,q,rot); + if(ComputeU) svd.m_matrixU.applyJacobiOnTheRight(p,q,rot.adjoint()); if(work_matrix.coeff(p,q) != Scalar(0)) { Scalar z = ei_abs(work_matrix.coeff(p,q)) / work_matrix.coeff(p,q); @@ -154,38 +155,34 @@ struct ei_svd_precondition_2x2_block_to_be_real if(ComputeU) svd.m_matrixU.col(q) *= ei_conj(z); } } - } + } }; template void ei_real_2x2_jacobi_svd(const MatrixType& matrix, int p, int q, - RealScalar *c_left, RealScalar *s_left, - RealScalar *c_right, RealScalar *s_right) + JacobiRotation *j_left, + JacobiRotation *j_right) { Matrix m; m << ei_real(matrix.coeff(p,p)), ei_real(matrix.coeff(p,q)), - ei_real(matrix.coeff(q,p)), ei_real(matrix.coeff(q,q)); - RealScalar c1, s1; + ei_real(matrix.coeff(q,p)), ei_real(matrix.coeff(q,q)); + JacobiRotation rot1; RealScalar t = m.coeff(0,0) + m.coeff(1,1); RealScalar d = m.coeff(1,0) - m.coeff(0,1); if(t == RealScalar(0)) { - c1 = 0; - s1 = d > 0 ? 1 : -1; + rot1.c() = 0; + rot1.s() = d > 0 ? 1 : -1; } else { RealScalar u = d / t; - c1 = RealScalar(1) / ei_sqrt(1 + ei_abs2(u)); - s1 = c1 * u; + rot1.c() = RealScalar(1) / ei_sqrt(1 + ei_abs2(u)); + rot1.s() = rot1.c() * u; } - m.applyJacobiOnTheLeft(0,1,c1,s1); - RealScalar c2, s2; - m.makeJacobi(0,1,&c2,&s2); - *c_left = c1*c2 + s1*s2; - *s_left = s1*c2 - c1*s2; - *c_right = c2; - *s_right = s2; + m.applyJacobiOnTheLeft(0,1,rot1); + m.makeJacobi(0,1,j_right); + *j_left = rot1 * j_right->transpose(); } template @@ -208,18 +205,18 @@ sweep_again: { ei_svd_precondition_2x2_block_to_be_real::run(work_matrix, *this, p, q); - RealScalar c_left, s_left, c_right, s_right; - ei_real_2x2_jacobi_svd(work_matrix, p, q, &c_left, &s_left, &c_right, &s_right); - - work_matrix.applyJacobiOnTheLeft(p,q,c_left,s_left); - if(ComputeU) m_matrixU.applyJacobiOnTheRight(p,q,c_left,-s_left); - - work_matrix.applyJacobiOnTheRight(p,q,c_right,s_right); - if(ComputeV) m_matrixV.applyJacobiOnTheRight(p,q,c_right,s_right); + JacobiRotation j_left, j_right; + ei_real_2x2_jacobi_svd(work_matrix, p, q, &j_left, &j_right); + + work_matrix.applyJacobiOnTheLeft(p,q,j_left); + if(ComputeU) m_matrixU.applyJacobiOnTheRight(p,q,j_left.transpose()); + + work_matrix.applyJacobiOnTheRight(p,q,j_right); + if(ComputeV) m_matrixV.applyJacobiOnTheRight(p,q,j_right); } } } - + RealScalar biggestOnDiag = work_matrix.diagonal().cwise().abs().maxCoeff(); RealScalar maxAllowedOffDiag = biggestOnDiag * precision; for(int p = 0; p < size; ++p) @@ -231,7 +228,7 @@ sweep_again: if(ei_abs(work_matrix.coeff(p,q)) > maxAllowedOffDiag) goto sweep_again; } - + for(int i = 0; i < size; ++i) { RealScalar a = ei_abs(work_matrix.coeff(i,i)); @@ -251,7 +248,7 @@ sweep_again: if(ComputeV) m_matrixV.col(pos).swap(m_matrixV.col(i)); } } - + m_isInitialized = true; return *this; } -- cgit v1.2.3 From 1e7a9ea70a35a2a375d9f1283f62a4e7feb7f933 Mon Sep 17 00:00:00 2001 From: Gael Guennebaud Date: Tue, 1 Sep 2009 13:35:44 +0200 Subject: fix issue #47: now m.noalias() = XXX properly resize m if needed --- Eigen/src/Core/Matrix.h | 10 +++++++++- test/product_extra.cpp | 21 +++++++-------------- test/product_notemporary.cpp | 37 ++++++++++++++++++------------------- 3 files changed, 34 insertions(+), 34 deletions(-) diff --git a/Eigen/src/Core/Matrix.h b/Eigen/src/Core/Matrix.h index d0603871a..53d10fd31 100644 --- a/Eigen/src/Core/Matrix.h +++ b/Eigen/src/Core/Matrix.h @@ -338,7 +338,15 @@ class Matrix */ EIGEN_STRONG_INLINE Matrix& operator=(const Matrix& other) { - return _set(other); + return _set(other); + } + + /** \sa MatrixBase::lazyAssign() */ + template + EIGEN_STRONG_INLINE Matrix& lazyAssign(const MatrixBase& other) + { + _resize_to_match(other); + return Base::lazyAssign(other.derived()); } template diff --git a/test/product_extra.cpp b/test/product_extra.cpp index 213dbced6..2eae73231 100644 --- a/test/product_extra.cpp +++ b/test/product_extra.cpp @@ -59,16 +59,13 @@ template void product_extra(const MatrixType& m) // r0 = ei_random(0,rows/2-1), // r1 = ei_random(rows/2,rows); - // all the expressions in this test should be compiled as a single matrix product - // TODO: add internal checks to verify that - - VERIFY_IS_APPROX(m3 = (m1 * m2.adjoint()).lazy(), m1 * m2.adjoint().eval()); - VERIFY_IS_APPROX(m3 = (m1.adjoint() * square.adjoint()).lazy(), m1.adjoint().eval() * square.adjoint().eval()); - VERIFY_IS_APPROX(m3 = (m1.adjoint() * m2).lazy(), m1.adjoint().eval() * m2); - VERIFY_IS_APPROX(m3 = ((s1 * m1.adjoint()) * m2).lazy(), (s1 * m1.adjoint()).eval() * m2); - VERIFY_IS_APPROX(m3 = ((- m1.adjoint() * s1) * (s3 * m2)).lazy(), (- m1.adjoint() * s1).eval() * (s3 * m2).eval()); - VERIFY_IS_APPROX(m3 = ((s2 * m1.adjoint() * s1) * m2).lazy(), (s2 * m1.adjoint() * s1).eval() * m2); - VERIFY_IS_APPROX(m3 = ((-m1*s2) * s1*m2.adjoint()).lazy(), (-m1*s2).eval() * (s1*m2.adjoint()).eval()); + VERIFY_IS_APPROX(m3.noalias() = m1 * m2.adjoint(), m1 * m2.adjoint().eval()); + VERIFY_IS_APPROX(m3.noalias() = (m1.adjoint() * square.adjoint(), m1.adjoint().eval() * square.adjoint().eval()); + VERIFY_IS_APPROX(m3.noalias() = (m1.adjoint() * m2, m1.adjoint().eval() * m2); + VERIFY_IS_APPROX(m3.noalias() = (s1 * m1.adjoint()) * m2, (s1 * m1.adjoint()).eval() * m2); + VERIFY_IS_APPROX(m3.noalias() = (- m1.adjoint() * s1) * (s3 * m2), (- m1.adjoint() * s1).eval() * (s3 * m2).eval()); + VERIFY_IS_APPROX(m3.noalias() = (s2 * m1.adjoint() * s1) * m2, (s2 * m1.adjoint() * s1).eval() * m2); + VERIFY_IS_APPROX(m3.noalias() = (-m1*s2) * s1*m2.adjoint(), (-m1*s2).eval() * (s1*m2.adjoint()).eval()); // a very tricky case where a scale factor has to be automatically conjugated: VERIFY_IS_APPROX( m1.adjoint() * (s1*m2).conjugate(), (m1.adjoint()).eval() * ((s1*m2).conjugate()).eval()); @@ -76,7 +73,6 @@ template void product_extra(const MatrixType& m) // test all possible conjugate combinations for the four matrix-vector product cases: -// std::cerr << "a\n"; VERIFY_IS_APPROX((-m1.conjugate() * s2) * (s1 * vc2), (-m1.conjugate()*s2).eval() * (s1 * vc2).eval()); VERIFY_IS_APPROX((-m1 * s2) * (s1 * vc2.conjugate()), @@ -84,7 +80,6 @@ template void product_extra(const MatrixType& m) VERIFY_IS_APPROX((-m1.conjugate() * s2) * (s1 * vc2.conjugate()), (-m1.conjugate()*s2).eval() * (s1 * vc2.conjugate()).eval()); -// std::cerr << "b\n"; VERIFY_IS_APPROX((s1 * vc2.transpose()) * (-m1.adjoint() * s2), (s1 * vc2.transpose()).eval() * (-m1.adjoint()*s2).eval()); VERIFY_IS_APPROX((s1 * vc2.adjoint()) * (-m1.transpose() * s2), @@ -92,7 +87,6 @@ template void product_extra(const MatrixType& m) VERIFY_IS_APPROX((s1 * vc2.adjoint()) * (-m1.adjoint() * s2), (s1 * vc2.adjoint()).eval() * (-m1.adjoint()*s2).eval()); -// std::cerr << "c\n"; VERIFY_IS_APPROX((-m1.adjoint() * s2) * (s1 * v1.transpose()), (-m1.adjoint()*s2).eval() * (s1 * v1.transpose()).eval()); VERIFY_IS_APPROX((-m1.transpose() * s2) * (s1 * v1.adjoint()), @@ -100,7 +94,6 @@ template void product_extra(const MatrixType& m) VERIFY_IS_APPROX((-m1.adjoint() * s2) * (s1 * v1.adjoint()), (-m1.adjoint()*s2).eval() * (s1 * v1.adjoint()).eval()); -// std::cerr << "d\n"; VERIFY_IS_APPROX((s1 * v1) * (-m1.conjugate() * s2), (s1 * v1).eval() * (-m1.conjugate()*s2).eval()); VERIFY_IS_APPROX((s1 * v1.conjugate()) * (-m1 * s2), diff --git a/test/product_notemporary.cpp b/test/product_notemporary.cpp index f6e8f4101..36a5a66ed 100644 --- a/test/product_notemporary.cpp +++ b/test/product_notemporary.cpp @@ -70,52 +70,51 @@ template void product_notemporary(const MatrixType& m) VERIFY_EVALUATION_COUNT( m3 = (m1 * m2.adjoint()), 1); VERIFY_EVALUATION_COUNT( m3 = (m1 * m2.adjoint()).lazy(), 0); - VERIFY_EVALUATION_COUNT( m3.noalias() = m1 * m2.adjoint(), 0); // NOTE in this case the slow product is used: // FIXME: VERIFY_EVALUATION_COUNT( m3.noalias() = s1 * (m1 * m2.transpose()), 0); - VERIFY_EVALUATION_COUNT( m3 = (s1 * m1 * s2 * m2.adjoint()).lazy(), 0); - VERIFY_EVALUATION_COUNT( m3 = (s1 * m1 * s2 * (m1*s3+m2*s2).adjoint()).lazy(), 1); - VERIFY_EVALUATION_COUNT( m3 = ((s1 * m1).adjoint() * s2 * m2).lazy(), 0); + VERIFY_EVALUATION_COUNT( m3.noalias() = s1 * m1 * s2 * m2.adjoint(), 0); + VERIFY_EVALUATION_COUNT( m3.noalias() = s1 * m1 * s2 * (m1*s3+m2*s2).adjoint(), 1); + VERIFY_EVALUATION_COUNT( m3.noalias() = (s1 * m1).adjoint() * s2 * m2, 0); VERIFY_EVALUATION_COUNT( m3 += (s1 * (-m1*s3).adjoint() * (s2 * m2 * s3)).lazy(), 0); VERIFY_EVALUATION_COUNT( m3.noalias() += s1 * (-m1*s3).adjoint() * (s2 * m2 * s3), 0); VERIFY_EVALUATION_COUNT( m3 -= (s1 * (m1.transpose() * m2)).lazy(), 0); VERIFY_EVALUATION_COUNT( m3.noalias() -= s1 * (m1.transpose() * m2), 0); - VERIFY_EVALUATION_COUNT(( m3.block(r0,r0,r1,r1) += (-m1.block(r0,c0,r1,c1) * (s2*m2.block(r0,c0,r1,c1)).adjoint()).lazy() ), 0); - VERIFY_EVALUATION_COUNT(( m3.block(r0,r0,r1,r1) -= (s1 * m1.block(r0,c0,r1,c1) * m2.block(c0,r0,c1,r1)).lazy() ), 0); + VERIFY_EVALUATION_COUNT(( m3.block(r0,r0,r1,r1).noalias() += -m1.block(r0,c0,r1,c1) * (s2*m2.block(r0,c0,r1,c1)).adjoint() ), 0); + VERIFY_EVALUATION_COUNT(( m3.block(r0,r0,r1,r1).noalias() -= s1 * m1.block(r0,c0,r1,c1) * m2.block(c0,r0,c1,r1) ), 0); // NOTE this is because the Block expression is not handled yet by our expression analyser VERIFY_EVALUATION_COUNT(( m3.block(r0,r0,r1,r1) = (s1 * m1.block(r0,c0,r1,c1) * (s1*m2).block(c0,r0,c1,r1)).lazy() ), 1); - VERIFY_EVALUATION_COUNT( m3 -= ((s1 * m1).template triangularView() * m2).lazy(), 0); - VERIFY_EVALUATION_COUNT( rm3 = ((s1 * m1.adjoint()).template triangularView() * (m2+m2)).lazy(), 1); - VERIFY_EVALUATION_COUNT( rm3 = ((s1 * m1.adjoint()).template triangularView() * m2.adjoint()).lazy(), 0); + VERIFY_EVALUATION_COUNT( m3.noalias() -= (s1 * m1).template triangularView() * m2, 0); + VERIFY_EVALUATION_COUNT( rm3.noalias() = (s1 * m1.adjoint()).template triangularView() * (m2+m2), 1); + VERIFY_EVALUATION_COUNT( rm3.noalias() = (s1 * m1.adjoint()).template triangularView() * m2.adjoint(), 0); - VERIFY_EVALUATION_COUNT( rm3.col(c0) = ((s1 * m1.adjoint()).template triangularView() * (s2*m2.row(c0)).adjoint()).lazy(), 0); + VERIFY_EVALUATION_COUNT( rm3.col(c0).noalias() = (s1 * m1.adjoint()).template triangularView() * (s2*m2.row(c0)).adjoint(), 0); VERIFY_EVALUATION_COUNT( m1.template triangularView().solveInPlace(m3), 0); VERIFY_EVALUATION_COUNT( m1.adjoint().template triangularView().solveInPlace(m3.transpose()), 0); - VERIFY_EVALUATION_COUNT( m3 -= ((s1 * m1).adjoint().template selfadjointView() * (-m2*s3).adjoint()).lazy(), 0); - VERIFY_EVALUATION_COUNT( m3 = (s2 * m2.adjoint() * (s1 * m1.adjoint()).template selfadjointView()).lazy(), 0); - VERIFY_EVALUATION_COUNT( rm3 = ((s1 * m1.adjoint()).template selfadjointView() * m2.adjoint()).lazy(), 0); + VERIFY_EVALUATION_COUNT( m3.noalias() -= (s1 * m1).adjoint().template selfadjointView() * (-m2*s3).adjoint(), 0); + VERIFY_EVALUATION_COUNT( m3.noalias() = s2 * m2.adjoint() * (s1 * m1.adjoint()).template selfadjointView(), 0); + VERIFY_EVALUATION_COUNT( rm3.noalias() = (s1 * m1.adjoint()).template selfadjointView() * m2.adjoint(), 0); - VERIFY_EVALUATION_COUNT( m3.col(c0) = ((s1 * m1).adjoint().template selfadjointView() * (-m2.row(c0)*s3).adjoint()).lazy(), 0); - VERIFY_EVALUATION_COUNT( m3.col(c0) -= ((s1 * m1).adjoint().template selfadjointView() * (-m2.row(c0)*s3).adjoint()).lazy(), 0); + VERIFY_EVALUATION_COUNT( m3.col(c0).noalias() = (s1 * m1).adjoint().template selfadjointView() * (-m2.row(c0)*s3).adjoint(), 0); + VERIFY_EVALUATION_COUNT( m3.col(c0).noalias() -= (s1 * m1).adjoint().template selfadjointView() * (-m2.row(c0)*s3).adjoint(), 0); - VERIFY_EVALUATION_COUNT( m3.block(r0,c0,r1,c1) += ((m1.block(r0,r0,r1,r1).template selfadjointView() * (s1*m2.block(r0,c0,r1,c1)) )).lazy(), 0); - VERIFY_EVALUATION_COUNT( m3.block(r0,c0,r1,c1) = ((m1.block(r0,r0,r1,r1).template selfadjointView() * m2.block(r0,c0,r1,c1) )).lazy(), 0); + VERIFY_EVALUATION_COUNT( m3.block(r0,c0,r1,c1).noalias() += m1.block(r0,r0,r1,r1).template selfadjointView() * (s1*m2.block(r0,c0,r1,c1)), 0); + VERIFY_EVALUATION_COUNT( m3.block(r0,c0,r1,c1).noalias() = m1.block(r0,r0,r1,r1).template selfadjointView() * m2.block(r0,c0,r1,c1), 0); VERIFY_EVALUATION_COUNT( m3.template selfadjointView().rankUpdate(m2.adjoint()), 0); m3.resize(1,1); - VERIFY_EVALUATION_COUNT( m3 = ((m1.block(r0,r0,r1,r1).template selfadjointView() * m2.block(r0,c0,r1,c1) )).lazy(), 0); + VERIFY_EVALUATION_COUNT( m3.noalias() = m1.block(r0,r0,r1,r1).template selfadjointView() * m2.block(r0,c0,r1,c1), 0); m3.resize(1,1); - VERIFY_EVALUATION_COUNT( m3 = ((m1.block(r0,r0,r1,r1).template triangularView() * m2.block(r0,c0,r1,c1) )).lazy(), 0); + VERIFY_EVALUATION_COUNT( m3.noalias() = m1.block(r0,r0,r1,r1).template triangularView() * m2.block(r0,c0,r1,c1), 0); } void test_product_notemporary() -- cgit v1.2.3 From 67ccc6b85167895924bc0a854da3b800dea68727 Mon Sep 17 00:00:00 2001 From: Gael Guennebaud Date: Tue, 1 Sep 2009 13:44:21 +0200 Subject: I've been too fast (again) --- test/product_extra.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/product_extra.cpp b/test/product_extra.cpp index 2eae73231..526dd210e 100644 --- a/test/product_extra.cpp +++ b/test/product_extra.cpp @@ -60,8 +60,8 @@ template void product_extra(const MatrixType& m) // r1 = ei_random(rows/2,rows); VERIFY_IS_APPROX(m3.noalias() = m1 * m2.adjoint(), m1 * m2.adjoint().eval()); - VERIFY_IS_APPROX(m3.noalias() = (m1.adjoint() * square.adjoint(), m1.adjoint().eval() * square.adjoint().eval()); - VERIFY_IS_APPROX(m3.noalias() = (m1.adjoint() * m2, m1.adjoint().eval() * m2); + VERIFY_IS_APPROX(m3.noalias() = m1.adjoint() * square.adjoint(), m1.adjoint().eval() * square.adjoint().eval()); + VERIFY_IS_APPROX(m3.noalias() = m1.adjoint() * m2, m1.adjoint().eval() * m2); VERIFY_IS_APPROX(m3.noalias() = (s1 * m1.adjoint()) * m2, (s1 * m1.adjoint()).eval() * m2); VERIFY_IS_APPROX(m3.noalias() = (- m1.adjoint() * s1) * (s3 * m2), (- m1.adjoint() * s1).eval() * (s3 * m2).eval()); VERIFY_IS_APPROX(m3.noalias() = (s2 * m1.adjoint() * s1) * m2, (s2 * m1.adjoint() * s1).eval() * m2); -- cgit v1.2.3 From 4d91229bdce3ab91ef25d853126989e4cd235b9f Mon Sep 17 00:00:00 2001 From: Gael Guennebaud Date: Tue, 1 Sep 2009 16:20:56 +0200 Subject: [mq]: eigensolver --- Eigen/QR | 2 + Eigen/src/QR/ComplexEigenSolver.h | 138 +++++++++++++++++++ Eigen/src/QR/ComplexSchur.h | 273 ++++++++++++++++++++++++++++++++++++++ test/CMakeLists.txt | 1 + test/eigensolver_complex.cpp | 70 ++++++++++ 5 files changed, 484 insertions(+) create mode 100644 Eigen/src/QR/ComplexEigenSolver.h create mode 100644 Eigen/src/QR/ComplexSchur.h create mode 100644 test/eigensolver_complex.cpp diff --git a/Eigen/QR b/Eigen/QR index 1cc94d8eb..4b49004c3 100644 --- a/Eigen/QR +++ b/Eigen/QR @@ -42,6 +42,8 @@ namespace Eigen { #include "src/QR/EigenSolver.h" #include "src/QR/SelfAdjointEigenSolver.h" #include "src/QR/HessenbergDecomposition.h" +#include "src/QR/ComplexSchur.h" +#include "src/QR/ComplexEigenSolver.h" // declare all classes for a given matrix type #define EIGEN_QR_MODULE_INSTANTIATE_TYPE(MATRIXTYPE,PREFIX) \ diff --git a/Eigen/src/QR/ComplexEigenSolver.h b/Eigen/src/QR/ComplexEigenSolver.h new file mode 100644 index 000000000..af47c2195 --- /dev/null +++ b/Eigen/src/QR/ComplexEigenSolver.h @@ -0,0 +1,138 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2009 Claire Maurice +// Copyright (C) 2009 Gael Guennebaud +// +// 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 . + +#ifndef EIGEN_COMPLEX_EIGEN_SOLVER_H +#define EIGEN_COMPLEX_EIGEN_SOLVER_H + +#define MAXITER 30 + +template class ComplexEigenSolver +{ + public: + typedef _MatrixType MatrixType; + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits::Real RealScalar; + typedef std::complex Complex; + typedef Matrix EigenvalueType; + typedef Matrix EigenvectorType; + + /** + * \brief Default Constructor. + * + * The default constructor is useful in cases in which the user intends to + * perform decompositions via ComplexEigenSolver::compute(const MatrixType&). + */ + ComplexEigenSolver() : m_eivec(), m_eivalues(), m_isInitialized(false) + {} + + ComplexEigenSolver(const MatrixType& matrix) + : m_eivec(matrix.rows(),matrix.cols()), + m_eivalues(matrix.cols()), + m_isInitialized(false) + { + compute(matrix); + } + + EigenvectorType eigenvectors(void) const + { + ei_assert(m_isInitialized && "ComplexEigenSolver is not initialized."); + return m_eivec; + } + + EigenvalueType eigenvalues() const + { + ei_assert(m_isInitialized && "ComplexEigenSolver is not initialized."); + return m_eivalues; + } + + void compute(const MatrixType& matrix); + + protected: + MatrixType m_eivec; + EigenvalueType m_eivalues; + bool m_isInitialized; +}; + + +template +void ComplexEigenSolver::compute(const MatrixType& matrix) +{ + assert(matrix.cols() == matrix.rows()); + int n = matrix.cols(); + m_eivalues.resize(n,1); + + RealScalar eps = epsilon(); + + // Reduce to complex Schur form + ComplexSchur schur(matrix); + + m_eivalues = schur.matrixT().diagonal(); + + m_eivec.setZero(); + + Scalar d2, z; + RealScalar norm = matrix.norm(); + + // compute the (normalized) eigenvectors + for(int k=n-1 ; k>=0 ; k--) + { + d2 = schur.matrixT().coeff(k,k); + m_eivec.coeffRef(k,k) = Scalar(1.0,0.0); + for(int i=k-1 ; i>=0 ; i--) + { + m_eivec.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)) + z.real() = eps * norm; + m_eivec.coeffRef(i,k) = m_eivec.coeff(i,k) / z; + + } + m_eivec.col(k).normalize(); + } + + m_eivec = schur.matrixU() * m_eivec; + m_isInitialized = true; + + // sort the eigenvalues + { + for (int i=0; i +// +// 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 . + +#ifndef EIGEN_COMPLEX_SCHUR_H +#define EIGEN_COMPLEX_SCHUR_H + +#define MAXITER 30 + +/** \ingroup QR + * + * \class ComplexShur + * + * \brief Performs a complex Shur decomposition of a real or complex square matrix + * + */ +template class ComplexSchur +{ + public: + typedef _MatrixType MatrixType; + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits::Real RealScalar; + typedef std::complex Complex; + typedef Matrix ComplexMatrixType; + + /** + * \brief Default Constructor. + * + * The default constructor is useful in cases in which the user intends to + * perform decompositions via ComplexSchur::compute(const MatrixType&). + */ + ComplexSchur() : m_matT(), m_matU(), m_isInitialized(false) + {} + + ComplexSchur(const MatrixType& matrix) + : m_matT(matrix.rows(),matrix.cols()), + m_matU(matrix.rows(),matrix.cols()), + m_isInitialized(false) + { + compute(matrix); + } + + ComplexMatrixType matrixU() const + { + ei_assert(m_isInitialized && "ComplexSchur is not initialized."); + return m_matU; + } + + ComplexMatrixType matrixT() const + { + ei_assert(m_isInitialized && "ComplexShur is not initialized."); + return m_matT; + } + + void compute(const MatrixType& matrix); + + protected: + ComplexMatrixType m_matT, m_matU; + bool m_isInitialized; +}; + +// computes the plane rotation G such that G' x |p| = | c s' |' |p| = |z| +// |q| |-s c' | |q| |0| +// and returns z if requested. Note that the returned c is real. +template void ei_make_givens(const std::complex& p, const std::complex& q, + JacobiRotation >& rot, std::complex* z=0) +{ + typedef std::complex Complex; + T scale, absx, absxy; + if(p==Complex(0)) + { + // return identity + rot.c() = Complex(1,0); + rot.s() = Complex(0,0); + if(z) *z = p; + } + else + { + scale = cnorm1(p); + absx = scale * ei_sqrt(ei_abs2(p/scale)); + scale = ei_abs(scale) + cnorm1(q); + absxy = scale * ei_sqrt((absx/scale)*(absx/scale) + ei_abs2(q/scale)); + rot.c() = Complex(absx / absxy); + Complex np = p/absx; + rot.s() = -ei_conj(np) * q / absxy; + if(z) *z = np * absxy; + } +} + +template +void ComplexSchur::compute(const MatrixType& matrix) +{ + assert(matrix.cols() == matrix.rows()); + int n = matrix.cols(); + + // Reduce to Hessenberg form + HessenbergDecomposition hess(matrix); + + m_matT = hess.matrixH(); + m_matU = hess.matrixQ(); + + int iu = m_matT.cols() - 1; + int il; + RealScalar d,sd,sf; + Complex c,b,disc,r1,r2,kappa; + + RealScalar eps = epsilon(); + + int iter = 0; + while(true) + { + //locate the range in which to iterate + while(iu > 0) + { + d = cnorm1(m_matT.coeffRef(iu,iu)) + cnorm1(m_matT.coeffRef(iu-1,iu-1)); + sd = cnorm1(m_matT.coeffRef(iu,iu-1)); + + if(sd >= eps * d) break; // FIXME : precision criterion ?? + + m_matT.coeffRef(iu,iu-1) = Complex(0); + iter = 0; + --iu; + } + if(iu==0) break; + iter++; + + if(iter >= MAXITER) + { + // FIXME : what to do when iter==MAXITER ?? + std::cerr << "MAXITER" << std::endl; + return; + } + + il = iu-1; + while( il > 0 ) + { + // check if the current 2x2 block on the diagonal is upper triangular + d = cnorm1(m_matT.coeffRef(il,il)) + cnorm1(m_matT.coeffRef(il-1,il-1)); + sd = cnorm1(m_matT.coeffRef(il,il-1)); + + if(sd < eps * d) break; // FIXME : precision criterion ?? + + --il; + } + + if( il != 0 ) m_matT.coeffRef(il,il-1) = Complex(0); + + // compute the shift (the normalization by sf is to avoid under/overflow) + Matrix t = m_matT.template block<2,2>(iu-1,iu-1); + sf = t.cwise().abs().sum(); + t /= sf; + + c = t.determinant(); + b = t.diagonal().sum(); + + disc = csqrt(b*b - RealScalar(4)*c); + + r1 = (b+disc)/RealScalar(2); + r2 = (b-disc)/RealScalar(2); + + if(cnorm1(r1) > cnorm1(r2)) + r2 = c/r1; + else + r1 = c/r2; + + if(cnorm1(r1-t.coeff(1,1)) < cnorm1(r2-t.coeff(1,1))) + kappa = sf * r1; + else + kappa = sf * r2; + + // perform the QR step using Givens rotations + JacobiRotation rot; + ei_make_givens(m_matT.coeff(il,il) - kappa, m_matT.coeff(il+1,il), rot); + + for(int i=il ; i +T cnorm1(const std::complex &Z) +{ + return(ei_abs(Z.real()) + ei_abs(Z.imag())); +} + +/** + * Computes the principal value of the square root of the complex \a z. + */ +template +std::complex csqrt(const std::complex &z) +{ + RealScalar t, tre, tim; + + t = ei_abs(z); + + if (ei_abs(z.real()) <= ei_abs(z.imag())) + { + // No cancellation in these formulas + tre = ei_sqrt(0.5*(t+z.real())); + tim = ei_sqrt(0.5*(t-z.real())); + } + else + { + // Stable computation of the above formulas + if (z.real() > 0) + { + tre = t + z.real(); + tim = ei_abs(z.imag())*ei_sqrt(0.5/tre); + tre = ei_sqrt(0.5*tre); + } + else + { + tim = t - z.real(); + tre = ei_abs(z.imag())*ei_sqrt(0.5/tim); + tim = ei_sqrt(0.5*tim); + } + } + if(z.imag() < 0) + tim = -tim; + + return (std::complex(tre,tim)); + +} + + +#endif // EIGEN_COMPLEX_SCHUR_H diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 896392188..56d36aa70 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -125,6 +125,7 @@ ei_add_test(qr_colpivoting) ei_add_test(qr_fullpivoting) ei_add_test(eigensolver_selfadjoint " " "${GSL_LIBRARIES}") ei_add_test(eigensolver_generic " " "${GSL_LIBRARIES}") +ei_add_test(eigensolver_complex) ei_add_test(svd) ei_add_test(jacobisvd ${EI_OFLAG}) ei_add_test(geo_orthomethods) diff --git a/test/eigensolver_complex.cpp b/test/eigensolver_complex.cpp new file mode 100644 index 000000000..32de327dd --- /dev/null +++ b/test/eigensolver_complex.cpp @@ -0,0 +1,70 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. Eigen itself is part of the KDE project. +// +// Copyright (C) 2008-2009 Gael Guennebaud +// +// 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 . + +#include "main.h" +#include +#include + +template void eigensolver(const MatrixType& m) +{ + /* this test covers the following files: + ComplexEigenSolver.h + */ + int rows = m.rows(); + int cols = m.cols(); + + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits::Real RealScalar; + typedef Matrix VectorType; + typedef Matrix RealVectorType; + typedef typename std::complex::Real> Complex; + + // RealScalar largerEps = 10*test_precision(); + + MatrixType a = MatrixType::Random(rows,cols); + MatrixType a1 = MatrixType::Random(rows,cols); + MatrixType symmA = a.adjoint() * a + a1.adjoint() * a1; + +// ComplexEigenSolver ei0(symmA); + +// VERIFY_IS_APPROX(symmA * ei0.eigenvectors(), ei0.eigenvectors() * ei0.eigenvalues().asDiagonal()); + +// a.imag().setZero(); +// std::cerr << a << "\n\n"; + ComplexEigenSolver ei1(a); +// exit(1); + VERIFY_IS_APPROX(a * ei1.eigenvectors(), ei1.eigenvectors() * ei1.eigenvalues().asDiagonal()); + +} + +void test_eigensolver_complex() +{ + for(int i = 0; i < g_repeat; i++) { +// CALL_SUBTEST( eigensolver(Matrix4cf()) ); +// CALL_SUBTEST( eigensolver(MatrixXcd(4,4)) ); + CALL_SUBTEST( eigensolver(MatrixXcd(6,6)) ); +// CALL_SUBTEST( eigensolver(MatrixXd(14,14)) ); + } +} + -- cgit v1.2.3 From 5b8ffa4d46958d691042f2537cba4dd52f795bdc Mon Sep 17 00:00:00 2001 From: Gael Guennebaud Date: Tue, 1 Sep 2009 16:35:23 +0200 Subject: clean a bit the previous commit which came from a patch queue, and since it was my first try of the patch queue feature I did not managed to apply it with a good commit message, so here you go: * Add a ComplexSchur decomposition class built on top of HessenbergDecomposition * Add a ComplexEigenSolver built on top of ComplexSchur There are still a couple of FIXME but at least they work for any reasonable matrices, still have to extend the unit tests to stress them with nasty matrices... --- test/eigensolver_complex.cpp | 21 ++++++--------------- test/product_extra.cpp | 2 +- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/test/eigensolver_complex.cpp b/test/eigensolver_complex.cpp index 32de327dd..dad5a69f8 100644 --- a/test/eigensolver_complex.cpp +++ b/test/eigensolver_complex.cpp @@ -29,7 +29,7 @@ template void eigensolver(const MatrixType& m) { /* this test covers the following files: - ComplexEigenSolver.h + ComplexEigenSolver.h, and indirectly ComplexSchur.h */ int rows = m.rows(); int cols = m.cols(); @@ -40,20 +40,13 @@ template void eigensolver(const MatrixType& m) typedef Matrix RealVectorType; typedef typename std::complex::Real> Complex; - // RealScalar largerEps = 10*test_precision(); - MatrixType a = MatrixType::Random(rows,cols); - MatrixType a1 = MatrixType::Random(rows,cols); - MatrixType symmA = a.adjoint() * a + a1.adjoint() * a1; - -// ComplexEigenSolver ei0(symmA); + MatrixType symmA = a.adjoint() * a; -// VERIFY_IS_APPROX(symmA * ei0.eigenvectors(), ei0.eigenvectors() * ei0.eigenvalues().asDiagonal()); + ComplexEigenSolver ei0(symmA); + VERIFY_IS_APPROX(symmA * ei0.eigenvectors(), ei0.eigenvectors() * ei0.eigenvalues().asDiagonal()); -// a.imag().setZero(); -// std::cerr << a << "\n\n"; ComplexEigenSolver ei1(a); -// exit(1); VERIFY_IS_APPROX(a * ei1.eigenvectors(), ei1.eigenvectors() * ei1.eigenvalues().asDiagonal()); } @@ -61,10 +54,8 @@ template void eigensolver(const MatrixType& m) void test_eigensolver_complex() { for(int i = 0; i < g_repeat; i++) { -// CALL_SUBTEST( eigensolver(Matrix4cf()) ); -// CALL_SUBTEST( eigensolver(MatrixXcd(4,4)) ); - CALL_SUBTEST( eigensolver(MatrixXcd(6,6)) ); -// CALL_SUBTEST( eigensolver(MatrixXd(14,14)) ); + CALL_SUBTEST( eigensolver(Matrix4cf()) ); + CALL_SUBTEST( eigensolver(MatrixXcd(14,14)) ); } } diff --git a/test/product_extra.cpp b/test/product_extra.cpp index 526dd210e..fcec362a5 100644 --- a/test/product_extra.cpp +++ b/test/product_extra.cpp @@ -59,7 +59,7 @@ template void product_extra(const MatrixType& m) // r0 = ei_random(0,rows/2-1), // r1 = ei_random(rows/2,rows); - VERIFY_IS_APPROX(m3.noalias() = m1 * m2.adjoint(), m1 * m2.adjoint().eval()); + VERIFY_IS_APPROX(m3.noalias() = m1 * m2.adjoint(), m1 * m2.adjoint().eval()); VERIFY_IS_APPROX(m3.noalias() = m1.adjoint() * square.adjoint(), m1.adjoint().eval() * square.adjoint().eval()); VERIFY_IS_APPROX(m3.noalias() = m1.adjoint() * m2, m1.adjoint().eval() * m2); VERIFY_IS_APPROX(m3.noalias() = (s1 * m1.adjoint()) * m2, (s1 * m1.adjoint()).eval() * m2); -- cgit v1.2.3 From 05ddd328490582d726b24a443ceabce336343eab Mon Sep 17 00:00:00 2001 From: Hauke Heibel Date: Tue, 1 Sep 2009 23:12:40 +0200 Subject: added missing JacobiRotation's ... --- Eigen/src/QR/SelfAdjointEigenSolver.h | 2 +- Eigen/src/SVD/SVD.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Eigen/src/QR/SelfAdjointEigenSolver.h b/Eigen/src/QR/SelfAdjointEigenSolver.h index 809a717b9..dd1736e28 100644 --- a/Eigen/src/QR/SelfAdjointEigenSolver.h +++ b/Eigen/src/QR/SelfAdjointEigenSolver.h @@ -380,7 +380,7 @@ static void ei_tridiagonal_qr_step(RealScalar* diag, RealScalar* subdiag, int st if (matrixQ) { Map > q(matrixQ,n,n); - q.applyJacobiOnTheRight(k,k+1,c,s); + q.applyJacobiOnTheRight(k,k+1,JacobiRotation(c,s)); } } } diff --git a/Eigen/src/SVD/SVD.h b/Eigen/src/SVD/SVD.h index 1a7e6c49a..095e18b3e 100644 --- a/Eigen/src/SVD/SVD.h +++ b/Eigen/src/SVD/SVD.h @@ -309,7 +309,7 @@ SVD& SVD::compute(const MatrixType& matrix) h = Scalar(1.0)/h; c = g*h; s = -f*h; - V.applyJacobiOnTheRight(i,nm,c,s); + V.applyJacobiOnTheRight(i,nm,JacobiRotation(c,s)); } } z = W[k]; @@ -351,7 +351,7 @@ SVD& SVD::compute(const MatrixType& matrix) g = g*c - x*s; h = y*s; y *= c; - V.applyJacobiOnTheRight(i,j,c,s); + V.applyJacobiOnTheRight(i,j,JacobiRotation(c,s)); z = pythag(f,h); W[j] = z; @@ -364,7 +364,7 @@ SVD& SVD::compute(const MatrixType& matrix) } f = c*g + s*y; x = c*y - s*g; - A.applyJacobiOnTheRight(i,j,c,s); + A.applyJacobiOnTheRight(i,j,JacobiRotation(c,s)); } rv1[l] = 0.0; rv1[k] = f; -- cgit v1.2.3 From 59f5bce41c5c32eebb75c51d92c9730f3ab9d350 Mon Sep 17 00:00:00 2001 From: Hauke Heibel Date: Tue, 1 Sep 2009 23:15:30 +0200 Subject: fix issue #49 --- test/main.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/main.h b/test/main.h index 8c84e525c..619fc9e06 100644 --- a/test/main.h +++ b/test/main.h @@ -30,6 +30,10 @@ #include #include +#ifdef NDEBUG +#undef NDEBUG +#endif + #ifndef EIGEN_TEST_FUNC #error EIGEN_TEST_FUNC must be defined #endif -- cgit v1.2.3 From c16d65f01585b51f41b715a22c43983faab4299a Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Wed, 2 Sep 2009 06:35:01 -0400 Subject: fix compilation errors in swap (could not swap with anything else than the exact same Matrix type) --- Eigen/src/Core/Matrix.h | 42 +++++++++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/Eigen/src/Core/Matrix.h b/Eigen/src/Core/Matrix.h index 53d10fd31..2fc38c812 100644 --- a/Eigen/src/Core/Matrix.h +++ b/Eigen/src/Core/Matrix.h @@ -139,6 +139,9 @@ class Matrix && SizeAtCompileTime!=Dynamic && ((sizeof(Scalar)*SizeAtCompileTime)%16)==0 }; EIGEN_MAKE_ALIGNED_OPERATOR_NEW_IF(NeedsToAlign) + Base& base() { return *static_cast(this); } + const Base& base() const { return *static_cast(this); } + EIGEN_STRONG_INLINE int rows() const { return m_storage.rows(); } EIGEN_STRONG_INLINE int cols() const { return m_storage.cols(); } @@ -487,13 +490,8 @@ class Matrix /** Override MatrixBase::swap() since for dynamic-sized matrices of same type it is enough to swap the * data pointers. */ - inline void swap(Matrix& other) - { - if (Base::SizeAtCompileTime==Dynamic) - m_storage.swap(other.m_storage); - else - this->Base::swap(other); - } + template + void swap(const MatrixBase& other); /** \name Map * These are convenience functions returning Map objects. The Map() static functions return unaligned Map objects, @@ -652,8 +650,38 @@ class Matrix m_storage.data()[0] = x; m_storage.data()[1] = y; } + + template + friend struct ei_matrix_swap_impl; +}; + +template::ret, + bool IsDynamicSize = MatrixType::SizeAtCompileTime==Dynamic> +struct ei_matrix_swap_impl +{ + static inline void run(MatrixType& matrix, MatrixBase& other) + { + matrix.base().swap(other); + } +}; + +template +struct ei_matrix_swap_impl +{ + static inline void run(MatrixType& matrix, MatrixBase& other) + { + matrix.m_storage.swap(other.derived().m_storage); + } }; +template +template +inline void Matrix<_Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols>::swap(const MatrixBase& other) +{ + ei_matrix_swap_impl::run(*this, *const_cast*>(&other)); +} + /** \defgroup matrixtypedefs Global matrix typedefs * * \ingroup Core_Module -- cgit v1.2.3 From e6b77bcc6bc915ec38640ecf414726fa2ba56fba Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Wed, 2 Sep 2009 06:36:55 -0400 Subject: JacobiSVD: implement general R-SVD using full-pivoting QR, so we now support any rectangular matrix size by reducing to the smaller of the two dimensions (which is also an optimization) --- Eigen/SVD | 2 +- Eigen/src/QR/FullPivotingHouseholderQR.h | 2 +- Eigen/src/SVD/JacobiSVD.h | 49 +++++++++++++++++++++++++------- test/jacobisvd.cpp | 8 ++++-- 4 files changed, 45 insertions(+), 16 deletions(-) diff --git a/Eigen/SVD b/Eigen/SVD index 01582310c..8d66d0736 100644 --- a/Eigen/SVD +++ b/Eigen/SVD @@ -1,7 +1,7 @@ #ifndef EIGEN_SVD_MODULE_H #define EIGEN_SVD_MODULE_H -#include "Core" +#include "QR" #include "Householder" #include "Jacobi" diff --git a/Eigen/src/QR/FullPivotingHouseholderQR.h b/Eigen/src/QR/FullPivotingHouseholderQR.h index cee41b152..52405170b 100644 --- a/Eigen/src/QR/FullPivotingHouseholderQR.h +++ b/Eigen/src/QR/FullPivotingHouseholderQR.h @@ -286,7 +286,7 @@ FullPivotingHouseholderQR& FullPivotingHouseholderQR::co m_cols_permutation.resize(matrix.cols()); int number_of_transpositions = 0; - RealScalar biggest; + RealScalar biggest(0); for (int k = 0; k < size; ++k) { diff --git a/Eigen/src/SVD/JacobiSVD.h b/Eigen/src/SVD/JacobiSVD.h index b3d289706..654f8981a 100644 --- a/Eigen/src/SVD/JacobiSVD.h +++ b/Eigen/src/SVD/JacobiSVD.h @@ -188,15 +188,42 @@ void ei_real_2x2_jacobi_svd(const MatrixType& matrix, int p, int q, template JacobiSVD& JacobiSVD::compute(const MatrixType& matrix) { - MatrixType work_matrix(matrix); - int size = matrix.rows(); - if(ComputeU) m_matrixU = MatrixUType::Identity(size,size); - if(ComputeV) m_matrixV = MatrixUType::Identity(size,size); - m_singularValues.resize(size); + MatrixType work_matrix; + int rows = matrix.rows(); + int cols = matrix.cols(); + int diagSize = std::min(rows, cols); + if(ComputeU) m_matrixU = MatrixUType::Zero(rows,rows); + if(ComputeV) m_matrixV = MatrixVType::Zero(cols,cols); + m_singularValues.resize(diagSize); const RealScalar precision = 2 * epsilon(); + if(rows > cols) + { + FullPivotingHouseholderQR qr(matrix); + work_matrix = qr.matrixQR().block(0,0,diagSize,diagSize).template triangularView(); + if(ComputeU) m_matrixU = qr.matrixQ(); + if(ComputeV) + for(int i = 0; i < cols; i++) + m_matrixV.coeffRef(qr.colsPermutation().coeff(i),i) = Scalar(1); + } + else if(rows < cols) + { + FullPivotingHouseholderQR qr(MatrixType(matrix.adjoint())); + work_matrix = qr.matrixQR().block(0,0,diagSize,diagSize).template triangularView().adjoint(); + if(ComputeV) m_matrixV = qr.matrixQ(); + if(ComputeU) + for(int i = 0; i < rows; i++) + m_matrixU.coeffRef(qr.colsPermutation().coeff(i),i) = Scalar(1); + + } + else + { + work_matrix = matrix; + if(ComputeU) m_matrixU.diagonal().setOnes(); + if(ComputeV) m_matrixV.diagonal().setOnes(); + } sweep_again: - for(int p = 1; p < size; ++p) + for(int p = 1; p < diagSize; ++p) { for(int q = 0; q < p; ++q) { @@ -219,27 +246,27 @@ sweep_again: RealScalar biggestOnDiag = work_matrix.diagonal().cwise().abs().maxCoeff(); RealScalar maxAllowedOffDiag = biggestOnDiag * precision; - for(int p = 0; p < size; ++p) + for(int p = 0; p < diagSize; ++p) { for(int q = 0; q < p; ++q) if(ei_abs(work_matrix.coeff(p,q)) > maxAllowedOffDiag) goto sweep_again; - for(int q = p+1; q < size; ++q) + for(int q = p+1; q < diagSize; ++q) if(ei_abs(work_matrix.coeff(p,q)) > maxAllowedOffDiag) goto sweep_again; } - for(int i = 0; i < size; ++i) + for(int i = 0; i < diagSize; ++i) { RealScalar a = ei_abs(work_matrix.coeff(i,i)); m_singularValues.coeffRef(i) = a; if(ComputeU && (a!=RealScalar(0))) m_matrixU.col(i) *= work_matrix.coeff(i,i)/a; } - for(int i = 0; i < size; i++) + for(int i = 0; i < diagSize; i++) { int pos; - m_singularValues.end(size-i).maxCoeff(&pos); + m_singularValues.end(diagSize-i).maxCoeff(&pos); if(pos) { pos += i; diff --git a/test/jacobisvd.cpp b/test/jacobisvd.cpp index 9a4d79e45..8b4c7584e 100644 --- a/test/jacobisvd.cpp +++ b/test/jacobisvd.cpp @@ -91,12 +91,14 @@ void test_jacobisvd() CALL_SUBTEST( svd(Matrix3f()) ); CALL_SUBTEST( svd(Matrix4d()) ); CALL_SUBTEST( svd(MatrixXf(50,50)) ); -// CALL_SUBTEST( svd(MatrixXd(14,7)) ); + CALL_SUBTEST( svd(MatrixXcd(14,7)) ); + CALL_SUBTEST( svd(MatrixXd(10,50)) ); + CALL_SUBTEST( svd(MatrixXcf(3,3)) ); CALL_SUBTEST( svd(MatrixXd(30,30)) ); } - CALL_SUBTEST( svd(MatrixXf(200,200)) ); - CALL_SUBTEST( svd(MatrixXcd(100,100)) ); + CALL_SUBTEST( svd(MatrixXf(300,200)) ); + CALL_SUBTEST( svd(MatrixXcd(100,150)) ); CALL_SUBTEST( svd_verify_assert() ); CALL_SUBTEST( svd_verify_assert() ); -- cgit v1.2.3 From ec20d583179bb2f01686c1c6fc0079ddee8ae4e2 Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Wed, 2 Sep 2009 16:56:48 -0400 Subject: * add serious unit test for swap * fix my stupidity in Matrix::swap() --- Eigen/src/Core/Matrix.h | 9 +++-- test/CMakeLists.txt | 1 + test/basicstuff.cpp | 9 ----- test/swap.cpp | 98 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 104 insertions(+), 13 deletions(-) create mode 100644 test/swap.cpp diff --git a/Eigen/src/Core/Matrix.h b/Eigen/src/Core/Matrix.h index 2fc38c812..cdc76c120 100644 --- a/Eigen/src/Core/Matrix.h +++ b/Eigen/src/Core/Matrix.h @@ -651,13 +651,12 @@ class Matrix m_storage.data()[1] = y; } - template + template friend struct ei_matrix_swap_impl; }; template::ret, - bool IsDynamicSize = MatrixType::SizeAtCompileTime==Dynamic> + bool IsSameType = ei_is_same_type::ret> struct ei_matrix_swap_impl { static inline void run(MatrixType& matrix, MatrixBase& other) @@ -667,10 +666,11 @@ struct ei_matrix_swap_impl }; template -struct ei_matrix_swap_impl +struct ei_matrix_swap_impl { static inline void run(MatrixType& matrix, MatrixBase& other) { + ei_assert(matrix.rows() == other.rows() && matrix.cols() == other.cols()); matrix.m_storage.swap(other.derived().m_storage); } }; @@ -679,6 +679,7 @@ template inline void Matrix<_Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols>::swap(const MatrixBase& other) { + ei_matrix_swap_impl::run(*this, *const_cast*>(&other)); } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 56d36aa70..6bbf82144 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -148,6 +148,7 @@ ei_add_test(sparse_product) ei_add_test(sparse_solvers " " "${SPARSE_LIBS}") ei_add_test(umeyama) ei_add_test(householder) +ei_add_test(swap) ei_add_property(EIGEN_TESTING_SUMMARY "CXX: ${CMAKE_CXX_COMPILER}\n") if(CMAKE_COMPILER_IS_GNUCXX) diff --git a/test/basicstuff.cpp b/test/basicstuff.cpp index 52aff93ee..29df99f9e 100644 --- a/test/basicstuff.cpp +++ b/test/basicstuff.cpp @@ -99,15 +99,6 @@ template void basicStuff(const MatrixType& m) MatrixType m4; VERIFY_IS_APPROX(m4 = m1,m1); - // test swap - m3 = m1; - m1.swap(m2); - VERIFY_IS_APPROX(m3, m2); - if(rows*cols>=3) - { - VERIFY_IS_NOT_APPROX(m3, m1); - } - m3.real() = m1.real(); VERIFY_IS_APPROX(static_cast(m3).real(), static_cast(m1).real()); VERIFY_IS_APPROX(static_cast(m3).real(), m1.real()); diff --git a/test/swap.cpp b/test/swap.cpp new file mode 100644 index 000000000..8b325992c --- /dev/null +++ b/test/swap.cpp @@ -0,0 +1,98 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2009 Benoit Jacob +// +// 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 . + +#define EIGEN_NO_STATIC_ASSERT +#include "main.h" + +template +struct other_matrix_type +{ + typedef int type; +}; + +template +struct other_matrix_type > +{ + typedef Matrix<_Scalar, _Rows, _Cols, _Options^RowMajor, _MaxRows, _MaxCols> type; +}; + +template void swap(const MatrixType& m) +{ + typedef typename other_matrix_type::type OtherMatrixType; + typedef typename MatrixType::Scalar Scalar; + + ei_assert((!ei_is_same_type::ret)); + int rows = m.rows(); + int cols = m.cols(); + + // construct 3 matrix guaranteed to be distinct + MatrixType m1 = MatrixType::Random(rows,cols); + MatrixType m2 = MatrixType::Random(rows,cols) + Scalar(100) * MatrixType::Identity(rows,cols); + OtherMatrixType m3 = OtherMatrixType::Random(rows,cols) + Scalar(200) * OtherMatrixType::Identity(rows,cols); + + MatrixType m1_copy = m1; + MatrixType m2_copy = m2; + OtherMatrixType m3_copy = m3; + + // test swapping 2 matrices of same type + m1.swap(m2); + VERIFY_IS_APPROX(m1,m2_copy); + VERIFY_IS_APPROX(m2,m1_copy); + m1 = m1_copy; + m2 = m2_copy; + + // test swapping 2 matrices of different types + m1.swap(m3); + VERIFY_IS_APPROX(m1,m3_copy); + VERIFY_IS_APPROX(m3,m1_copy); + m1 = m1_copy; + m3 = m3_copy; + + // test swapping matrix with expression + m1.swap(m2.block(0,0,rows,cols)); + VERIFY_IS_APPROX(m1,m2_copy); + VERIFY_IS_APPROX(m2,m1_copy); + m1 = m1_copy; + m2 = m2_copy; + + // test swapping two expressions of different types + m1.transpose().swap(m3.transpose()); + VERIFY_IS_APPROX(m1,m3_copy); + VERIFY_IS_APPROX(m3,m1_copy); + m1 = m1_copy; + m3 = m3_copy; + + // test assertion on mismatching size -- matrix case + VERIFY_RAISES_ASSERT(m1.swap(m1.row(0))); + // test assertion on mismatching size -- xpr case + VERIFY_RAISES_ASSERT(m1.row(0).swap(m1)); +} + +void test_swap() +{ + CALL_SUBTEST( swap(Matrix3f()) ); // fixed size, no vectorization + CALL_SUBTEST( swap(Matrix4d()) ); // fixed size, possible vectorization + CALL_SUBTEST( swap(MatrixXd(3,3)) ); // dyn size, no vectorization + CALL_SUBTEST( swap(MatrixXf(30,30)) ); // dyn size, possible vectorization +} -- cgit v1.2.3 From 4a8258369a28eb340a1789bc05322475c7ace8f9 Mon Sep 17 00:00:00 2001 From: Gael Guennebaud Date: Wed, 2 Sep 2009 13:37:15 +0200 Subject: much simpler fix for Matrix::swap --- Eigen/src/Core/Matrix.h | 39 +++++++-------------------------------- 1 file changed, 7 insertions(+), 32 deletions(-) diff --git a/Eigen/src/Core/Matrix.h b/Eigen/src/Core/Matrix.h index cdc76c120..16c0ee4ac 100644 --- a/Eigen/src/Core/Matrix.h +++ b/Eigen/src/Core/Matrix.h @@ -490,8 +490,13 @@ class Matrix /** Override MatrixBase::swap() since for dynamic-sized matrices of same type it is enough to swap the * data pointers. */ - template - void swap(const MatrixBase& other); + using Base::swap; + inline void swap(Matrix& other) + { + ei_assert(matrix.rows() == other.rows() && matrix.cols() == other.cols()); + m_storage.swap(other.m_storage); + // FIXME what about using this->Base::swap(other); for fixed size ? + } /** \name Map * These are convenience functions returning Map objects. The Map() static functions return unaligned Map objects, @@ -650,38 +655,8 @@ class Matrix m_storage.data()[0] = x; m_storage.data()[1] = y; } - - template - friend struct ei_matrix_swap_impl; -}; - -template::ret> -struct ei_matrix_swap_impl -{ - static inline void run(MatrixType& matrix, MatrixBase& other) - { - matrix.base().swap(other); - } }; -template -struct ei_matrix_swap_impl -{ - static inline void run(MatrixType& matrix, MatrixBase& other) - { - ei_assert(matrix.rows() == other.rows() && matrix.cols() == other.cols()); - matrix.m_storage.swap(other.derived().m_storage); - } -}; - -template -template -inline void Matrix<_Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols>::swap(const MatrixBase& other) -{ - - ei_matrix_swap_impl::run(*this, *const_cast*>(&other)); -} /** \defgroup matrixtypedefs Global matrix typedefs * -- cgit v1.2.3 From 496ea63972f15c5bc7d43a7a4dc987b6cd33d176 Mon Sep 17 00:00:00 2001 From: Gael Guennebaud Date: Wed, 2 Sep 2009 14:08:33 +0200 Subject: fix wrong assert --- Eigen/src/Core/Matrix.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Eigen/src/Core/Matrix.h b/Eigen/src/Core/Matrix.h index 16c0ee4ac..caa8d4be6 100644 --- a/Eigen/src/Core/Matrix.h +++ b/Eigen/src/Core/Matrix.h @@ -493,7 +493,7 @@ class Matrix using Base::swap; inline void swap(Matrix& other) { - ei_assert(matrix.rows() == other.rows() && matrix.cols() == other.cols()); + ei_assert(rows() == other.rows() && cols() == other.cols()); m_storage.swap(other.m_storage); // FIXME what about using this->Base::swap(other); for fixed size ? } -- cgit v1.2.3 From b83654b5d01155c4ea875090f0779b99241a91a4 Mon Sep 17 00:00:00 2001 From: Gael Guennebaud Date: Wed, 2 Sep 2009 15:04:10 +0200 Subject: * rename JacobiRotation => PlanarRotation * move the makeJacobi and make_givens_* to PlanarRotation * rename applyJacobi* => apply* --- Eigen/src/Core/MathFunctions.h | 4 + Eigen/src/Core/MatrixBase.h | 9 +- Eigen/src/Core/util/ForwardDeclarations.h | 2 +- Eigen/src/Jacobi/Jacobi.h | 203 +++++++++++++++++++++--------- Eigen/src/QR/ComplexSchur.h | 59 ++------- Eigen/src/QR/SelfAdjointEigenSolver.h | 45 ++----- Eigen/src/SVD/JacobiSVD.h | 28 ++--- Eigen/src/SVD/SVD.h | 10 +- 8 files changed, 193 insertions(+), 167 deletions(-) diff --git a/Eigen/src/Core/MathFunctions.h b/Eigen/src/Core/MathFunctions.h index 1570f01e0..40edf4a3c 100644 --- a/Eigen/src/Core/MathFunctions.h +++ b/Eigen/src/Core/MathFunctions.h @@ -116,6 +116,7 @@ inline float ei_imag(float) { return 0.f; } inline float ei_conj(float x) { return x; } inline float ei_abs(float x) { return std::abs(x); } inline float ei_abs2(float x) { return x*x; } +inline float ei_norm1(float x) { return ei_abs(x); } inline float ei_sqrt(float x) { return std::sqrt(x); } inline float ei_exp(float x) { return std::exp(x); } inline float ei_log(float x) { return std::log(x); } @@ -164,6 +165,7 @@ inline double ei_imag(double) { return 0.; } inline double ei_conj(double x) { return x; } inline double ei_abs(double x) { return std::abs(x); } inline double ei_abs2(double x) { return x*x; } +inline double ei_norm1(double x) { return ei_abs(x); } inline double ei_sqrt(double x) { return std::sqrt(x); } inline double ei_exp(double x) { return std::exp(x); } inline double ei_log(double x) { return std::log(x); } @@ -212,6 +214,7 @@ inline float& ei_imag_ref(std::complex& x) { return reinterpret_cast ei_conj(const std::complex& x) { return std::conj(x); } inline float ei_abs(const std::complex& x) { return std::abs(x); } inline float ei_abs2(const std::complex& x) { return std::norm(x); } +inline float ei_norm1(const std::complex &x) { return(ei_abs(x.real()) + ei_abs(x.imag())); } inline std::complex ei_exp(std::complex x) { return std::exp(x); } inline std::complex ei_sin(std::complex x) { return std::sin(x); } inline std::complex ei_cos(std::complex x) { return std::cos(x); } @@ -248,6 +251,7 @@ inline double& ei_imag_ref(std::complex& x) { return reinterpret_cast ei_conj(const std::complex& x) { return std::conj(x); } inline double ei_abs(const std::complex& x) { return std::abs(x); } inline double ei_abs2(const std::complex& x) { return std::norm(x); } +inline double ei_norm1(const std::complex &x) { return(ei_abs(x.real()) + ei_abs(x.imag())); } inline std::complex ei_exp(std::complex x) { return std::exp(x); } inline std::complex ei_sin(std::complex x) { return std::sin(x); } inline std::complex ei_cos(std::complex x) { return std::cos(x); } diff --git a/Eigen/src/Core/MatrixBase.h b/Eigen/src/Core/MatrixBase.h index c2945ab46..9ac964168 100644 --- a/Eigen/src/Core/MatrixBase.h +++ b/Eigen/src/Core/MatrixBase.h @@ -803,11 +803,10 @@ template class MatrixBase ///////// Jacobi module ///////// - template - void applyJacobiOnTheLeft(int p, int q, const JacobiRotation& j); - template - void applyJacobiOnTheRight(int p, int q, const JacobiRotation& j); - bool makeJacobi(int p, int q, JacobiRotation *j) const; + template + void applyOnTheLeft(int p, int q, const PlanarRotation& j); + template + void applyOnTheRight(int p, int q, const PlanarRotation& j); #ifdef EIGEN_MATRIXBASE_PLUGIN #include EIGEN_MATRIXBASE_PLUGIN diff --git a/Eigen/src/Core/util/ForwardDeclarations.h b/Eigen/src/Core/util/ForwardDeclarations.h index 18d3af7c5..c5f27d80b 100644 --- a/Eigen/src/Core/util/ForwardDeclarations.h +++ b/Eigen/src/Core/util/ForwardDeclarations.h @@ -123,7 +123,7 @@ template class SVD; template class JacobiSVD; template class LLT; template class LDLT; -template class JacobiRotation; +template class PlanarRotation; // Geometry module: template class RotationBase; diff --git a/Eigen/src/Jacobi/Jacobi.h b/Eigen/src/Jacobi/Jacobi.h index 24fb7e782..c38d4583f 100644 --- a/Eigen/src/Jacobi/Jacobi.h +++ b/Eigen/src/Jacobi/Jacobi.h @@ -27,97 +27,72 @@ #define EIGEN_JACOBI_H /** \ingroup Jacobi - * \class JacobiRotation + * \class PlanarRotation * \brief Represents a rotation in the plane from a cosine-sine pair. * - * This class represents a Jacobi rotation which is also known as a Givens rotation. + * This class represents a Jacobi or Givens rotation. * This is a 2D clock-wise rotation in the plane \c J of angle \f$ \theta \f$ defined by * its cosine \c c and sine \c s as follow: * \f$ J = \left ( \begin{array}{cc} c & \overline s \\ -s & \overline c \end{array} \right ) \f$ * - * \sa MatrixBase::makeJacobi(), MatrixBase::applyJacobiOnTheLeft(), MatrixBase::applyJacobiOnTheRight() + * \sa MatrixBase::makeJacobi(), MatrixBase::applyOnTheLeft(), MatrixBase::applyOnTheRight() */ -template class JacobiRotation +template class PlanarRotation { public: + typedef typename NumTraits::Real RealScalar; + /** Default constructor without any initialization. */ - JacobiRotation() {} + PlanarRotation() {} - /** Construct a Jacobi rotation from a cosine-sine pair (\a c, \c s). */ - JacobiRotation(const Scalar& c, const Scalar& s) : m_c(c), m_s(s) {} + /** Construct a planar rotation from a cosine-sine pair (\a c, \c s). */ + PlanarRotation(const Scalar& c, const Scalar& s) : m_c(c), m_s(s) {} Scalar& c() { return m_c; } Scalar c() const { return m_c; } Scalar& s() { return m_s; } Scalar s() const { return m_s; } - /** Concatenates two Jacobi rotation */ - JacobiRotation operator*(const JacobiRotation& other) + /** Concatenates two planar rotation */ + PlanarRotation operator*(const PlanarRotation& other) { - return JacobiRotation(m_c * other.m_c - ei_conj(m_s) * other.m_s, + return PlanarRotation(m_c * other.m_c - ei_conj(m_s) * other.m_s, ei_conj(m_c * ei_conj(other.m_s) + ei_conj(m_s) * ei_conj(other.m_c))); } /** Returns the transposed transformation */ - JacobiRotation transpose() const { return JacobiRotation(m_c, -ei_conj(m_s)); } + PlanarRotation transpose() const { return PlanarRotation(m_c, -ei_conj(m_s)); } /** Returns the adjoint transformation */ - JacobiRotation adjoint() const { return JacobiRotation(ei_conj(m_c), -m_s); } + PlanarRotation adjoint() const { return PlanarRotation(ei_conj(m_c), -m_s); } - protected: - Scalar m_c, m_s; -}; + template + bool makeJacobi(const MatrixBase&, int p, int q); + bool makeJacobi(RealScalar x, Scalar y, RealScalar z); -/** Applies the clock wise 2D rotation \a j to the set of 2D vectors of cordinates \a x and \a y: - * \f$ \left ( \begin{array}{cc} x \\ y \end{array} \right ) = J \left ( \begin{array}{cc} x \\ y \end{array} \right ) \f$ - * - * \sa MatrixBase::applyJacobiOnTheLeft(), MatrixBase::applyJacobiOnTheRight() - */ -template -void ei_apply_rotation_in_the_plane(VectorX& _x, VectorY& _y, const JacobiRotation& j); + void makeGivens(const Scalar& p, const Scalar& q, Scalar* z=0); -/** Applies the rotation in the plane \a j to the rows \a p and \a q of \c *this, i.e., it computes B = J * B, - * with \f$ B = \left ( \begin{array}{cc} \text{*this.row}(p) \\ \text{*this.row}(q) \end{array} \right ) \f$. - * - * \sa class JacobiRotation, MatrixBase::applyJacobiOnTheRight(), ei_apply_rotation_in_the_plane() - */ -template -template -inline void MatrixBase::applyJacobiOnTheLeft(int p, int q, const JacobiRotation& j) -{ - RowXpr x(row(p)); - RowXpr y(row(q)); - ei_apply_rotation_in_the_plane(x, y, j); -} + protected: + void makeGivens(const Scalar& p, const Scalar& q, Scalar* z, ei_meta_true); + void makeGivens(const Scalar& p, const Scalar& q, Scalar* z, ei_meta_false); -/** Applies the rotation in the plane \a j to the columns \a p and \a q of \c *this, i.e., it computes B = B * J - * with \f$ B = \left ( \begin{array}{cc} \text{*this.col}(p) & \text{*this.col}(q) \end{array} \right ) \f$. - * - * \sa class JacobiRotation, MatrixBase::applyJacobiOnTheLeft(), ei_apply_rotation_in_the_plane() - */ -template -template -inline void MatrixBase::applyJacobiOnTheRight(int p, int q, const JacobiRotation& j) -{ - ColXpr x(col(p)); - ColXpr y(col(q)); - ei_apply_rotation_in_the_plane(x, y, j.transpose()); -} + Scalar m_c, m_s; +}; -/** Computes the Jacobi rotation \a J such that applying \a J on both the right and left sides of the 2x2 matrix +/** Makes \c *this as a Jacobi rotation \a J such that applying \a J on both the right and left sides of the 2x2 matrix * \f$ B = \left ( \begin{array}{cc} x & y \\ * & z \end{array} \right )\f$ yields * a diagonal matrix \f$ A = J^* B J \f$ * - * \sa MatrixBase::makeJacobi(), MatrixBase::applyJacobiOnTheLeft(), MatrixBase::applyJacobiOnTheRight() + * \sa MatrixBase::makeJacobi(), MatrixBase::applyOnTheLeft(), MatrixBase::applyOnTheRight() */ template -bool ei_makeJacobi(typename NumTraits::Real x, Scalar y, typename NumTraits::Real z, JacobiRotation *j) +bool PlanarRotation::makeJacobi(RealScalar x, Scalar y, RealScalar z) { typedef typename NumTraits::Real RealScalar; if(y == Scalar(0)) { - j->c() = Scalar(1); - j->s() = Scalar(0); + m_c = Scalar(1); + m_s = Scalar(0); return false; } else @@ -135,26 +110,132 @@ bool ei_makeJacobi(typename NumTraits::Real x, Scalar y, typename NumTra } RealScalar sign_t = t > 0 ? 1 : -1; RealScalar n = RealScalar(1) / ei_sqrt(ei_abs2(t)+1); - j->s() = - sign_t * (ei_conj(y) / ei_abs(y)) * ei_abs(t) * n; - j->c() = n; + m_s = - sign_t * (ei_conj(y) / ei_abs(y)) * ei_abs(t) * n; + m_c = n; return true; } } -/** Computes the Jacobi rotation \a J such that applying \a J on both the right and left sides of the 2x2 matrix +/** Makes \c *this as a Jacobi rotation \c J such that applying \a J on both the right and left sides of the 2x2 matrix * \f$ B = \left ( \begin{array}{cc} \text{this}_{pp} & \text{this}_{pq} \\ * & \text{this}_{qq} \end{array} \right )\f$ yields * a diagonal matrix \f$ A = J^* B J \f$ * - * \sa MatrixBase::ei_make_jacobi(), MatrixBase::applyJacobiOnTheLeft(), MatrixBase::applyJacobiOnTheRight() + * \sa PlanarRotation::makeJacobi(RealScalar, Scalar, RealScalar), MatrixBase::applyOnTheLeft(), MatrixBase::applyOnTheRight() */ +template template -inline bool MatrixBase::makeJacobi(int p, int q, JacobiRotation *j) const +inline bool PlanarRotation::makeJacobi(const MatrixBase& m, int p, int q) +{ + return makeJacobi(ei_real(m.coeff(p,p)), m.coeff(p,q), ei_real(m.coeff(q,q))); +} + +/** Makes \c *this as a Givens rotation \c G such that applying \f$ G^* \f$ to the left of the vector + * \f$ V = \left ( \begin{array}{c} p \\ q \end{array} \right )\f$ yields: + * \f$ G^* V = \left ( \begin{array}{c} z \\ 0 \end{array} \right )\f$. + * + * The value of \a z is returned if \a z is not null (the default is null). + * Also note that G is built such that the cosine is always real. + * + * \sa MatrixBase::applyOnTheLeft(), MatrixBase::applyOnTheRight() + */ +template +void PlanarRotation::makeGivens(const Scalar& p, const Scalar& q, Scalar* z) { - return ei_makeJacobi(ei_real(coeff(p,p)), coeff(p,q), ei_real(coeff(q,q)), j); + makeGivens(p, q, z, typename ei_meta_if::IsComplex, ei_meta_true, ei_meta_false>::ret()); } -template -void /*EIGEN_DONT_INLINE*/ ei_apply_rotation_in_the_plane(VectorX& _x, VectorY& _y, const JacobiRotation& j) + +// specialization for complexes +template +void PlanarRotation::makeGivens(const Scalar& p, const Scalar& q, Scalar* z, ei_meta_true) +{ + RealScalar scale, absx, absxy; + if(q==Scalar(0)) + { + // return identity + m_c = Scalar(1); + m_s = Scalar(0); + if(z) *z = p; + } + else + { + scale = ei_norm1(p); + absx = scale * ei_sqrt(ei_abs2(p/scale)); + scale = ei_abs(scale) + ei_norm1(q); + absxy = scale * ei_sqrt((absx/scale)*(absx/scale) + ei_abs2(q/scale)); + m_c = Scalar(absx / absxy); + Scalar np = p/absx; + m_s = -ei_conj(np) * q / absxy; + if(z) *z = np * absxy; + } +} + +// specialization for reals +template +void PlanarRotation::makeGivens(const Scalar& p, const Scalar& q, Scalar* z, ei_meta_false) +{ + // from Golub's "Matrix Computations", algorithm 5.1.3 + if(q==0) + { + m_c = 1; m_s = 0; + } + else if(ei_abs(q)>ei_abs(p)) + { + Scalar t = -p/q; + m_s = Scalar(1)/ei_sqrt(1+t*t); + m_c = m_s * t; + } + else + { + Scalar t = -q/p; + m_c = Scalar(1)/ei_sqrt(1+t*t); + m_s = m_c * t; + } +} + +/**************************************************************************************** +* Implementation of MatrixBase methods +/***************************************************************************************/ + +/** Applies the clock wise 2D rotation \a j to the set of 2D vectors of cordinates \a x and \a y: + * \f$ \left ( \begin{array}{cc} x \\ y \end{array} \right ) = J \left ( \begin{array}{cc} x \\ y \end{array} \right ) \f$ + * + * \sa MatrixBase::applyOnTheLeft(), MatrixBase::applyOnTheRight() + */ +template +void ei_apply_rotation_in_the_plane(VectorX& _x, VectorY& _y, const PlanarRotation& j); + +/** Applies the rotation in the plane \a j to the rows \a p and \a q of \c *this, i.e., it computes B = J * B, + * with \f$ B = \left ( \begin{array}{cc} \text{*this.row}(p) \\ \text{*this.row}(q) \end{array} \right ) \f$. + * + * \sa class PlanarRotation, MatrixBase::applyOnTheRight(), ei_apply_rotation_in_the_plane() + */ +template +template +inline void MatrixBase::applyOnTheLeft(int p, int q, const PlanarRotation& j) +{ + RowXpr x(row(p)); + RowXpr y(row(q)); + ei_apply_rotation_in_the_plane(x, y, j); +} + +/** Applies the rotation in the plane \a j to the columns \a p and \a q of \c *this, i.e., it computes B = B * J + * with \f$ B = \left ( \begin{array}{cc} \text{*this.col}(p) & \text{*this.col}(q) \end{array} \right ) \f$. + * + * \sa class PlanarRotation, MatrixBase::applyOnTheLeft(), ei_apply_rotation_in_the_plane() + */ +template +template +inline void MatrixBase::applyOnTheRight(int p, int q, const PlanarRotation& j) +{ + ColXpr x(col(p)); + ColXpr y(col(q)); + ei_apply_rotation_in_the_plane(x, y, j.transpose()); +} + + +template +void /*EIGEN_DONT_INLINE*/ ei_apply_rotation_in_the_plane(VectorX& _x, VectorY& _y, const PlanarRotation& j) { typedef typename VectorX::Scalar Scalar; ei_assert(_x.size() == _y.size()); diff --git a/Eigen/src/QR/ComplexSchur.h b/Eigen/src/QR/ComplexSchur.h index a0b954d49..153826811 100644 --- a/Eigen/src/QR/ComplexSchur.h +++ b/Eigen/src/QR/ComplexSchur.h @@ -80,34 +80,6 @@ template class ComplexSchur bool m_isInitialized; }; -// computes the plane rotation G such that G' x |p| = | c s' |' |p| = |z| -// |q| |-s c' | |q| |0| -// and returns z if requested. Note that the returned c is real. -template void ei_make_givens(const std::complex& p, const std::complex& q, - JacobiRotation >& rot, std::complex* z=0) -{ - typedef std::complex Complex; - T scale, absx, absxy; - if(p==Complex(0)) - { - // return identity - rot.c() = Complex(1,0); - rot.s() = Complex(0,0); - if(z) *z = p; - } - else - { - scale = cnorm1(p); - absx = scale * ei_sqrt(ei_abs2(p/scale)); - scale = ei_abs(scale) + cnorm1(q); - absxy = scale * ei_sqrt((absx/scale)*(absx/scale) + ei_abs2(q/scale)); - rot.c() = Complex(absx / absxy); - Complex np = p/absx; - rot.s() = -ei_conj(np) * q / absxy; - if(z) *z = np * absxy; - } -} - template void ComplexSchur::compute(const MatrixType& matrix) { @@ -133,8 +105,8 @@ void ComplexSchur::compute(const MatrixType& matrix) //locate the range in which to iterate while(iu > 0) { - d = cnorm1(m_matT.coeffRef(iu,iu)) + cnorm1(m_matT.coeffRef(iu-1,iu-1)); - sd = cnorm1(m_matT.coeffRef(iu,iu-1)); + d = ei_norm1(m_matT.coeffRef(iu,iu)) + ei_norm1(m_matT.coeffRef(iu-1,iu-1)); + sd = ei_norm1(m_matT.coeffRef(iu,iu-1)); if(sd >= eps * d) break; // FIXME : precision criterion ?? @@ -156,8 +128,8 @@ void ComplexSchur::compute(const MatrixType& matrix) while( il > 0 ) { // check if the current 2x2 block on the diagonal is upper triangular - d = cnorm1(m_matT.coeffRef(il,il)) + cnorm1(m_matT.coeffRef(il-1,il-1)); - sd = cnorm1(m_matT.coeffRef(il,il-1)); + d = ei_norm1(m_matT.coeffRef(il,il)) + ei_norm1(m_matT.coeffRef(il-1,il-1)); + sd = ei_norm1(m_matT.coeffRef(il,il-1)); if(sd < eps * d) break; // FIXME : precision criterion ?? @@ -179,32 +151,32 @@ void ComplexSchur::compute(const MatrixType& matrix) r1 = (b+disc)/RealScalar(2); r2 = (b-disc)/RealScalar(2); - if(cnorm1(r1) > cnorm1(r2)) + if(ei_norm1(r1) > ei_norm1(r2)) r2 = c/r1; else r1 = c/r2; - if(cnorm1(r1-t.coeff(1,1)) < cnorm1(r2-t.coeff(1,1))) + if(ei_norm1(r1-t.coeff(1,1)) < ei_norm1(r2-t.coeff(1,1))) kappa = sf * r1; else kappa = sf * r2; // perform the QR step using Givens rotations - JacobiRotation rot; - ei_make_givens(m_matT.coeff(il,il) - kappa, m_matT.coeff(il+1,il), rot); + PlanarRotation rot; + rot.makeGivens(m_matT.coeff(il,il) - kappa, m_matT.coeff(il+1,il)); for(int i=il ; i::compute(const MatrixType& matrix) m_isInitialized = true; } -// norm1 of complex numbers -template -T cnorm1(const std::complex &Z) -{ - return(ei_abs(Z.real()) + ei_abs(Z.imag())); -} - /** * Computes the principal value of the square root of the complex \a z. */ diff --git a/Eigen/src/QR/SelfAdjointEigenSolver.h b/Eigen/src/QR/SelfAdjointEigenSolver.h index dd1736e28..580b042f6 100644 --- a/Eigen/src/QR/SelfAdjointEigenSolver.h +++ b/Eigen/src/QR/SelfAdjointEigenSolver.h @@ -135,28 +135,6 @@ template class SelfAdjointEigenSolver #ifndef EIGEN_HIDE_HEAVY_CODE -// from Golub's "Matrix Computations", algorithm 5.1.3 -template -static void ei_givens_rotation(Scalar a, Scalar b, Scalar& c, Scalar& s) -{ - if (b==0) - { - c = 1; s = 0; - } - else if (ei_abs(b)>ei_abs(a)) - { - Scalar t = -a/b; - s = Scalar(1)/ei_sqrt(1+t*t); - c = s * t; - } - else - { - Scalar t = -b/a; - c = Scalar(1)/ei_sqrt(1+t*t); - s = c * t; - } -} - /** \internal * * \qr_module @@ -353,34 +331,33 @@ static void ei_tridiagonal_qr_step(RealScalar* diag, RealScalar* subdiag, int st for (int k = start; k < end; ++k) { - RealScalar c, s; - ei_givens_rotation(x, z, c, s); + PlanarRotation rot; + rot.makeGivens(x, z); // do T = G' T G - RealScalar sdk = s * diag[k] + c * subdiag[k]; - RealScalar dkp1 = s * subdiag[k] + c * diag[k+1]; + RealScalar sdk = rot.s() * diag[k] + rot.c() * subdiag[k]; + RealScalar dkp1 = rot.s() * subdiag[k] + rot.c() * diag[k+1]; - diag[k] = c * (c * diag[k] - s * subdiag[k]) - s * (c * subdiag[k] - s * diag[k+1]); - diag[k+1] = s * sdk + c * dkp1; - subdiag[k] = c * sdk - s * dkp1; + diag[k] = rot.c() * (rot.c() * diag[k] - rot.s() * subdiag[k]) - rot.s() * (rot.c() * subdiag[k] - rot.s() * diag[k+1]); + diag[k+1] = rot.s() * sdk + rot.c() * dkp1; + subdiag[k] = rot.c() * sdk - rot.s() * dkp1; if (k > start) - subdiag[k - 1] = c * subdiag[k-1] - s * z; + subdiag[k - 1] = rot.c() * subdiag[k-1] - rot.s() * z; x = subdiag[k]; if (k < end - 1) { - z = -s * subdiag[k+1]; - subdiag[k + 1] = c * subdiag[k+1]; + z = -rot.s() * subdiag[k+1]; + subdiag[k + 1] = rot.c() * subdiag[k+1]; } // apply the givens rotation to the unit matrix Q = Q * G - // G only modifies the two columns k and k+1 if (matrixQ) { Map > q(matrixQ,n,n); - q.applyJacobiOnTheRight(k,k+1,JacobiRotation(c,s)); + q.applyOnTheRight(k,k+1,rot); } } } diff --git a/Eigen/src/SVD/JacobiSVD.h b/Eigen/src/SVD/JacobiSVD.h index 654f8981a..ca359832b 100644 --- a/Eigen/src/SVD/JacobiSVD.h +++ b/Eigen/src/SVD/JacobiSVD.h @@ -125,7 +125,7 @@ struct ei_svd_precondition_2x2_block_to_be_real static void run(MatrixType& work_matrix, JacobiSVD& svd, int p, int q) { Scalar z; - JacobiRotation rot; + PlanarRotation rot; RealScalar n = ei_sqrt(ei_abs2(work_matrix.coeff(p,p)) + ei_abs2(work_matrix.coeff(q,p))); if(n==0) { @@ -140,8 +140,8 @@ struct ei_svd_precondition_2x2_block_to_be_real { rot.c() = ei_conj(work_matrix.coeff(p,p)) / n; rot.s() = work_matrix.coeff(q,p) / n; - work_matrix.applyJacobiOnTheLeft(p,q,rot); - if(ComputeU) svd.m_matrixU.applyJacobiOnTheRight(p,q,rot.adjoint()); + work_matrix.applyOnTheLeft(p,q,rot); + if(ComputeU) svd.m_matrixU.applyOnTheRight(p,q,rot.adjoint()); if(work_matrix.coeff(p,q) != Scalar(0)) { Scalar z = ei_abs(work_matrix.coeff(p,q)) / work_matrix.coeff(p,q); @@ -160,13 +160,13 @@ struct ei_svd_precondition_2x2_block_to_be_real template void ei_real_2x2_jacobi_svd(const MatrixType& matrix, int p, int q, - JacobiRotation *j_left, - JacobiRotation *j_right) + PlanarRotation *j_left, + PlanarRotation *j_right) { Matrix m; m << ei_real(matrix.coeff(p,p)), ei_real(matrix.coeff(p,q)), ei_real(matrix.coeff(q,p)), ei_real(matrix.coeff(q,q)); - JacobiRotation rot1; + PlanarRotation rot1; RealScalar t = m.coeff(0,0) + m.coeff(1,1); RealScalar d = m.coeff(1,0) - m.coeff(0,1); if(t == RealScalar(0)) @@ -180,8 +180,8 @@ void ei_real_2x2_jacobi_svd(const MatrixType& matrix, int p, int q, rot1.c() = RealScalar(1) / ei_sqrt(1 + ei_abs2(u)); rot1.s() = rot1.c() * u; } - m.applyJacobiOnTheLeft(0,1,rot1); - m.makeJacobi(0,1,j_right); + m.applyOnTheLeft(0,1,rot1); + j_right->makeJacobi(m,0,1); *j_left = rot1 * j_right->transpose(); } @@ -214,7 +214,7 @@ JacobiSVD& JacobiSVD::compute(const Ma if(ComputeU) for(int i = 0; i < rows; i++) m_matrixU.coeffRef(qr.colsPermutation().coeff(i),i) = Scalar(1); - + } else { @@ -232,14 +232,14 @@ sweep_again: { ei_svd_precondition_2x2_block_to_be_real::run(work_matrix, *this, p, q); - JacobiRotation j_left, j_right; + PlanarRotation j_left, j_right; ei_real_2x2_jacobi_svd(work_matrix, p, q, &j_left, &j_right); - work_matrix.applyJacobiOnTheLeft(p,q,j_left); - if(ComputeU) m_matrixU.applyJacobiOnTheRight(p,q,j_left.transpose()); + work_matrix.applyOnTheLeft(p,q,j_left); + if(ComputeU) m_matrixU.applyOnTheRight(p,q,j_left.transpose()); - work_matrix.applyJacobiOnTheRight(p,q,j_right); - if(ComputeV) m_matrixV.applyJacobiOnTheRight(p,q,j_right); + work_matrix.applyOnTheRight(p,q,j_right); + if(ComputeV) m_matrixV.applyOnTheRight(p,q,j_right); } } } diff --git a/Eigen/src/SVD/SVD.h b/Eigen/src/SVD/SVD.h index 095e18b3e..da01cf396 100644 --- a/Eigen/src/SVD/SVD.h +++ b/Eigen/src/SVD/SVD.h @@ -309,7 +309,7 @@ SVD& SVD::compute(const MatrixType& matrix) h = Scalar(1.0)/h; c = g*h; s = -f*h; - V.applyJacobiOnTheRight(i,nm,JacobiRotation(c,s)); + V.applyOnTheRight(i,nm,PlanarRotation(c,s)); } } z = W[k]; @@ -342,7 +342,7 @@ SVD& SVD::compute(const MatrixType& matrix) y = W[i]; h = s*g; g = c*g; - + z = pythag(f,h); rv1[j] = z; c = f/z; @@ -351,8 +351,8 @@ SVD& SVD::compute(const MatrixType& matrix) g = g*c - x*s; h = y*s; y *= c; - V.applyJacobiOnTheRight(i,j,JacobiRotation(c,s)); - + V.applyOnTheRight(i,j,PlanarRotation(c,s)); + z = pythag(f,h); W[j] = z; // Rotation can be arbitrary if z = 0. @@ -364,7 +364,7 @@ SVD& SVD::compute(const MatrixType& matrix) } f = c*g + s*y; x = c*y - s*g; - A.applyJacobiOnTheRight(i,j,JacobiRotation(c,s)); + A.applyOnTheRight(i,j,PlanarRotation(c,s)); } rv1[l] = 0.0; rv1[k] = f; -- cgit v1.2.3 From 7586f7f706dbeeeed431d63e6d5990f8cb773caa Mon Sep 17 00:00:00 2001 From: Gael Guennebaud Date: Wed, 2 Sep 2009 15:18:11 +0200 Subject: fix #51 (bad use of std::complex::real) --- Eigen/src/Jacobi/Jacobi.h | 2 +- Eigen/src/QR/ComplexEigenSolver.h | 2 +- Eigen/src/QR/ComplexSchur.h | 89 +++++++++++++++++++-------------------- 3 files changed, 46 insertions(+), 47 deletions(-) diff --git a/Eigen/src/Jacobi/Jacobi.h b/Eigen/src/Jacobi/Jacobi.h index c38d4583f..66ba06b19 100644 --- a/Eigen/src/Jacobi/Jacobi.h +++ b/Eigen/src/Jacobi/Jacobi.h @@ -195,7 +195,7 @@ void PlanarRotation::makeGivens(const Scalar& p, const Scalar& q, Scalar /**************************************************************************************** * Implementation of MatrixBase methods -/***************************************************************************************/ +****************************************************************************************/ /** Applies the clock wise 2D rotation \a j to the set of 2D vectors of cordinates \a x and \a y: * \f$ \left ( \begin{array}{cc} x \\ y \end{array} \right ) = J \left ( \begin{array}{cc} x \\ y \end{array} \right ) \f$ diff --git a/Eigen/src/QR/ComplexEigenSolver.h b/Eigen/src/QR/ComplexEigenSolver.h index af47c2195..6caa6bc2d 100644 --- a/Eigen/src/QR/ComplexEigenSolver.h +++ b/Eigen/src/QR/ComplexEigenSolver.h @@ -107,7 +107,7 @@ void ComplexEigenSolver::compute(const MatrixType& matrix) 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)) - z.real() = eps * norm; + ei_real_ref(z) = eps * norm; m_eivec.coeffRef(i,k) = m_eivec.coeff(i,k) / z; } diff --git a/Eigen/src/QR/ComplexSchur.h b/Eigen/src/QR/ComplexSchur.h index 153826811..b60be5430 100644 --- a/Eigen/src/QR/ComplexSchur.h +++ b/Eigen/src/QR/ComplexSchur.h @@ -80,6 +80,43 @@ template class ComplexSchur bool m_isInitialized; }; +/** Computes the principal value of the square root of the complex \a z. */ +template +std::complex ei_sqrt(const std::complex &z) +{ + RealScalar t, tre, tim; + + t = ei_abs(z); + + if (ei_abs(ei_real(z)) <= ei_abs(ei_real(z))) + { + // No cancellation in these formulas + tre = ei_sqrt(0.5*(t + ei_real(z))); + tim = ei_sqrt(0.5*(t - ei_real(z))); + } + else + { + // Stable computation of the above formulas + if (z.real() > 0) + { + tre = t + z.real(); + tim = ei_abs(ei_imag(z))*ei_sqrt(0.5/tre); + tre = ei_sqrt(0.5*tre); + } + else + { + tim = t - z.real(); + tre = ei_abs(ei_imag(z))*ei_sqrt(0.5/tim); + tim = ei_sqrt(0.5*tim); + } + } + if(z.imag() < 0) + tim = -tim; + + return (std::complex(tre,tim)); + +} + template void ComplexSchur::compute(const MatrixType& matrix) { @@ -146,7 +183,7 @@ void ComplexSchur::compute(const MatrixType& matrix) c = t.determinant(); b = t.diagonal().sum(); - disc = csqrt(b*b - RealScalar(4)*c); + disc = ei_sqrt(b*b - RealScalar(4)*c); r1 = (b+disc)/RealScalar(2); r2 = (b-disc)/RealScalar(2); @@ -183,56 +220,18 @@ void ComplexSchur::compute(const MatrixType& matrix) } // FIXME : is it necessary ? + /* for(int i=0 ; i -std::complex csqrt(const std::complex &z) -{ - RealScalar t, tre, tim; - - t = ei_abs(z); - - if (ei_abs(z.real()) <= ei_abs(z.imag())) - { - // No cancellation in these formulas - tre = ei_sqrt(0.5*(t+z.real())); - tim = ei_sqrt(0.5*(t-z.real())); - } - else - { - // Stable computation of the above formulas - if (z.real() > 0) - { - tre = t + z.real(); - tim = ei_abs(z.imag())*ei_sqrt(0.5/tre); - tre = ei_sqrt(0.5*tre); - } - else - { - tim = t - z.real(); - tre = ei_abs(z.imag())*ei_sqrt(0.5/tim); - tim = ei_sqrt(0.5*tim); - } - } - if(z.imag() < 0) - tim = -tim; - - return (std::complex(tre,tim)); + m_isInitialized = true; } - #endif // EIGEN_COMPLEX_SCHUR_H -- cgit v1.2.3 From 7d18c30641a57cde5246614e3f7dd88fe867a7b0 Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Thu, 3 Sep 2009 01:25:40 -0400 Subject: finally the first version was the good one... --- Eigen/src/Core/Matrix.h | 38 +++++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/Eigen/src/Core/Matrix.h b/Eigen/src/Core/Matrix.h index caa8d4be6..2fc38c812 100644 --- a/Eigen/src/Core/Matrix.h +++ b/Eigen/src/Core/Matrix.h @@ -490,13 +490,8 @@ class Matrix /** Override MatrixBase::swap() since for dynamic-sized matrices of same type it is enough to swap the * data pointers. */ - using Base::swap; - inline void swap(Matrix& other) - { - ei_assert(rows() == other.rows() && cols() == other.cols()); - m_storage.swap(other.m_storage); - // FIXME what about using this->Base::swap(other); for fixed size ? - } + template + void swap(const MatrixBase& other); /** \name Map * These are convenience functions returning Map objects. The Map() static functions return unaligned Map objects, @@ -655,8 +650,37 @@ class Matrix m_storage.data()[0] = x; m_storage.data()[1] = y; } + + template + friend struct ei_matrix_swap_impl; +}; + +template::ret, + bool IsDynamicSize = MatrixType::SizeAtCompileTime==Dynamic> +struct ei_matrix_swap_impl +{ + static inline void run(MatrixType& matrix, MatrixBase& other) + { + matrix.base().swap(other); + } }; +template +struct ei_matrix_swap_impl +{ + static inline void run(MatrixType& matrix, MatrixBase& other) + { + matrix.m_storage.swap(other.derived().m_storage); + } +}; + +template +template +inline void Matrix<_Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols>::swap(const MatrixBase& other) +{ + ei_matrix_swap_impl::run(*this, *const_cast*>(&other)); +} /** \defgroup matrixtypedefs Global matrix typedefs * -- cgit v1.2.3 From 89557ac41d50196f17d48ada5137fcf435abe73a Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Thu, 3 Sep 2009 02:50:42 -0400 Subject: introduce EIGEN_SIZE_MIN now we should check if some EIGEN_ENUM_MIN usage needs to be replaced by that... potential bug when using mixed-size matrice --- Eigen/src/Core/util/Macros.h | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/Eigen/src/Core/util/Macros.h b/Eigen/src/Core/util/Macros.h index 4e00df759..ec8337e33 100644 --- a/Eigen/src/Core/util/Macros.h +++ b/Eigen/src/Core/util/Macros.h @@ -260,11 +260,11 @@ using Base::operator +=; \ using Base::operator -=; \ using Base::operator *=; \ using Base::operator /=; \ -EIGEN_STRONG_INLINE Derived& operator=(const Derived& other) \ -{ \ - return Base::operator=(other); \ -} -#endif +EIGEN_STRONG_INLINE Derived& operator=(const Derived& other) \ +{ \ + return Base::operator=(other); \ +} +#endif #define _EIGEN_GENERIC_PUBLIC_INTERFACE(Derived, BaseClass) \ typedef BaseClass Base; \ @@ -287,6 +287,9 @@ enum { RowsAtCompileTime = Eigen::ei_traits::RowsAtCompileTime, \ _EIGEN_GENERIC_PUBLIC_INTERFACE(Derived, Eigen::MatrixBase) #define EIGEN_ENUM_MIN(a,b) (((int)a <= (int)b) ? (int)a : (int)b) +#define EIGEN_SIZE_MIN(a,b) (((int)a == 1 || (int)b == 1) ? 1 \ + : ((int)a == Dynamic || (int)b == Dynamic) ? Dynamic \ + : ((int)a <= (int)b) ? (int)a : (int)b) #define EIGEN_ENUM_MAX(a,b) (((int)a >= (int)b) ? (int)a : (int)b) #define EIGEN_LOGICAL_XOR(a,b) (((a) || (b)) && !((a) && (b))) -- cgit v1.2.3 From 7aa6fd362558718304937ddfd60232c9802d69be Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Thu, 3 Sep 2009 02:53:51 -0400 Subject: big reorganization in JacobiSVD: - R-SVD preconditioning now done with meta selectors to avoid compiling useless code - SVD options now honored, with options to hint "at least as many rows as cols" etc... - fix compilation in bad cases (rectangular and fixed-size) - the check for termination is now done on the fly, no more goto (should have done that earlier!) --- Eigen/src/Core/util/Constants.h | 9 ++ Eigen/src/QR/FullPivotingHouseholderQR.h | 6 +- Eigen/src/SVD/JacobiSVD.h | 184 +++++++++++++++++++++---------- test/jacobisvd.cpp | 37 +++---- 4 files changed, 156 insertions(+), 80 deletions(-) diff --git a/Eigen/src/Core/util/Constants.h b/Eigen/src/Core/util/Constants.h index 21e7f62c3..affc1d478 100644 --- a/Eigen/src/Core/util/Constants.h +++ b/Eigen/src/Core/util/Constants.h @@ -238,6 +238,15 @@ enum { OnTheRight = 2 }; +// options for SVD decomposition +enum { + SkipU = 0x1, + SkipV = 0x2, + AtLeastAsManyRowsAsCols = 0x4, + AtLeastAsManyColsAsRows = 0x8, + Square = AtLeastAsManyRowsAsCols | AtLeastAsManyColsAsRows +}; + /* the following could as well be written: * enum NoChange_t { NoChange }; * but it feels dangerous to disambiguate overloaded functions on enum/integer types. diff --git a/Eigen/src/QR/FullPivotingHouseholderQR.h b/Eigen/src/QR/FullPivotingHouseholderQR.h index 52405170b..0d542cf7a 100644 --- a/Eigen/src/QR/FullPivotingHouseholderQR.h +++ b/Eigen/src/QR/FullPivotingHouseholderQR.h @@ -67,12 +67,10 @@ template class FullPivotingHouseholderQR * The default constructor is useful in cases in which the user intends to * perform decompositions via FullPivotingHouseholderQR::compute(const MatrixType&). */ - FullPivotingHouseholderQR() : m_qr(), m_hCoeffs(), m_isInitialized(false) {} + FullPivotingHouseholderQR() : m_isInitialized(false) {} FullPivotingHouseholderQR(const MatrixType& matrix) - : m_qr(matrix.rows(), matrix.cols()), - m_hCoeffs(std::min(matrix.rows(),matrix.cols())), - m_isInitialized(false) + : m_isInitialized(false) { compute(matrix); } diff --git a/Eigen/src/SVD/JacobiSVD.h b/Eigen/src/SVD/JacobiSVD.h index ca359832b..2801ee077 100644 --- a/Eigen/src/SVD/JacobiSVD.h +++ b/Eigen/src/SVD/JacobiSVD.h @@ -33,8 +33,11 @@ * \brief Jacobi SVD decomposition of a square matrix * * \param MatrixType the type of the matrix of which we are computing the SVD decomposition - * \param ComputeU whether the U matrix should be computed - * \param ComputeV whether the V matrix should be computed + * \param Options a bit field of flags offering the following options: \c SkipU and \c SkipV allow to skip the computation of + * the unitaries \a U and \a V respectively; \c AtLeastAsManyRowsAsCols and \c AtLeastAsManyRowsAsCols allows + * to hint the compiler to only generate the corresponding code paths; \c Square is equivantent to the combination of + * the latter two bits and is useful when you know that the matrix is square. Note that when this information can + * be automatically deduced from the matrix type (e.g. a Matrix3f is always square), Eigen does it for you. * * \sa MatrixBase::jacobiSvd() */ @@ -44,14 +47,14 @@ template class JacobiSVD typedef typename MatrixType::Scalar Scalar; typedef typename NumTraits::Real RealScalar; enum { - ComputeU = 1, - ComputeV = 1, + ComputeU = (Options & SkipU) == 0, + ComputeV = (Options & SkipV) == 0, RowsAtCompileTime = MatrixType::RowsAtCompileTime, ColsAtCompileTime = MatrixType::ColsAtCompileTime, - DiagSizeAtCompileTime = EIGEN_ENUM_MIN(RowsAtCompileTime,ColsAtCompileTime), + DiagSizeAtCompileTime = EIGEN_SIZE_MIN(RowsAtCompileTime,ColsAtCompileTime), MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime, MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime, - MaxDiagSizeAtCompileTime = EIGEN_ENUM_MIN(MaxRowsAtCompileTime,MaxColsAtCompileTime), + MaxDiagSizeAtCompileTime = EIGEN_SIZE_MIN(MaxRowsAtCompileTime,MaxColsAtCompileTime), MatrixOptions = MatrixType::Options }; @@ -65,9 +68,12 @@ template class JacobiSVD MatrixOptions, MaxColsAtCompileTime, MaxColsAtCompileTime>, DummyMatrixType>::ret MatrixVType; typedef Matrix SingularValuesType; + MatrixOptions, MaxDiagSizeAtCompileTime, 1> SingularValuesType; typedef Matrix RowType; typedef Matrix ColType; + typedef Matrix + WorkMatrixType; public: @@ -92,7 +98,7 @@ template class JacobiSVD return m_singularValues; } - const MatrixUType& matrixV() const + const MatrixVType& matrixV() const { ei_assert(m_isInitialized && "JacobiSVD is not initialized."); return m_matrixV; @@ -106,12 +112,17 @@ template class JacobiSVD template friend struct ei_svd_precondition_2x2_block_to_be_real; + template + friend struct ei_svd_precondition_if_more_rows_than_cols; + template + friend struct ei_svd_precondition_if_more_cols_than_rows; }; template::IsComplex> struct ei_svd_precondition_2x2_block_to_be_real { - static void run(MatrixType&, JacobiSVD&, int, int) {} + typedef JacobiSVD SVD; + static void run(typename SVD::WorkMatrixType&, JacobiSVD&, int, int) {} }; template @@ -120,9 +131,8 @@ struct ei_svd_precondition_2x2_block_to_be_real typedef JacobiSVD SVD; typedef typename MatrixType::Scalar Scalar; typedef typename MatrixType::RealScalar RealScalar; - enum { ComputeU = SVD::ComputeU, ComputeV = SVD::ComputeV }; - static void run(MatrixType& work_matrix, JacobiSVD& svd, int p, int q) + static void run(typename SVD::WorkMatrixType& work_matrix, JacobiSVD& svd, int p, int q) { Scalar z; PlanarRotation rot; @@ -185,10 +195,94 @@ void ei_real_2x2_jacobi_svd(const MatrixType& matrix, int p, int q, *j_left = rot1 * j_right->transpose(); } +templateMatrixType::ColsAtCompileTime)> +struct ei_svd_precondition_if_more_rows_than_cols +{ + typedef JacobiSVD SVD; + static bool run(const MatrixType&, typename SVD::WorkMatrixType&, JacobiSVD&) { return false; } +}; + +template +struct ei_svd_precondition_if_more_rows_than_cols +{ + typedef JacobiSVD SVD; + typedef typename MatrixType::Scalar Scalar; + typedef typename MatrixType::RealScalar RealScalar; + enum { ComputeU = SVD::ComputeU, ComputeV = SVD::ComputeV }; + static bool run(const MatrixType& matrix, typename SVD::WorkMatrixType& work_matrix, SVD& svd) + { + int rows = matrix.rows(); + int cols = matrix.cols(); + int diagSize = cols; + if(rows > cols) + { + FullPivotingHouseholderQR qr(matrix); + work_matrix = qr.matrixQR().block(0,0,diagSize,diagSize).template triangularView(); + if(ComputeU) svd.m_matrixU = qr.matrixQ(); + if(ComputeV) + for(int i = 0; i < cols; i++) + svd.m_matrixV.coeffRef(qr.colsPermutation().coeff(i),i) = Scalar(1); + return true; + } + else return false; + } +}; + +templateMatrixType::RowsAtCompileTime)> +struct ei_svd_precondition_if_more_cols_than_rows +{ + typedef JacobiSVD SVD; + static bool run(const MatrixType&, typename SVD::WorkMatrixType&, JacobiSVD&) { return false; } +}; + +template +struct ei_svd_precondition_if_more_cols_than_rows +{ + typedef JacobiSVD SVD; + typedef typename MatrixType::Scalar Scalar; + typedef typename MatrixType::RealScalar RealScalar; + enum { + ComputeU = SVD::ComputeU, + ComputeV = SVD::ComputeV, + RowsAtCompileTime = SVD::RowsAtCompileTime, + ColsAtCompileTime = SVD::ColsAtCompileTime, + MaxRowsAtCompileTime = SVD::MaxRowsAtCompileTime, + MaxColsAtCompileTime = SVD::MaxColsAtCompileTime, + MatrixOptions = SVD::MatrixOptions + }; + + static bool run(const MatrixType& matrix, typename SVD::WorkMatrixType& work_matrix, SVD& svd) + { + int rows = matrix.rows(); + int cols = matrix.cols(); + int diagSize = rows; + if(cols > rows) + { + typedef Matrix + TransposeTypeWithSameStorageOrder; + FullPivotingHouseholderQR qr(matrix.adjoint()); + work_matrix = qr.matrixQR().block(0,0,diagSize,diagSize).template triangularView().adjoint(); + if(ComputeV) svd.m_matrixV = qr.matrixQ(); + if(ComputeU) + for(int i = 0; i < rows; i++) + svd.m_matrixU.coeffRef(qr.colsPermutation().coeff(i),i) = Scalar(1); + return true; + } + else return false; + } +}; + template JacobiSVD& JacobiSVD::compute(const MatrixType& matrix) { - MatrixType work_matrix; + WorkMatrixType work_matrix; int rows = matrix.rows(); int cols = matrix.cols(); int diagSize = std::min(rows, cols); @@ -197,65 +291,41 @@ JacobiSVD& JacobiSVD::compute(const Ma m_singularValues.resize(diagSize); const RealScalar precision = 2 * epsilon(); - if(rows > cols) + if(!ei_svd_precondition_if_more_rows_than_cols::run(matrix, work_matrix, *this) + && !ei_svd_precondition_if_more_cols_than_rows::run(matrix, work_matrix, *this)) { - FullPivotingHouseholderQR qr(matrix); - work_matrix = qr.matrixQR().block(0,0,diagSize,diagSize).template triangularView(); - if(ComputeU) m_matrixU = qr.matrixQ(); - if(ComputeV) - for(int i = 0; i < cols; i++) - m_matrixV.coeffRef(qr.colsPermutation().coeff(i),i) = Scalar(1); - } - else if(rows < cols) - { - FullPivotingHouseholderQR qr(MatrixType(matrix.adjoint())); - work_matrix = qr.matrixQR().block(0,0,diagSize,diagSize).template triangularView().adjoint(); - if(ComputeV) m_matrixV = qr.matrixQ(); - if(ComputeU) - for(int i = 0; i < rows; i++) - m_matrixU.coeffRef(qr.colsPermutation().coeff(i),i) = Scalar(1); - - } - else - { - work_matrix = matrix; + work_matrix = matrix.block(0,0,diagSize,diagSize); if(ComputeU) m_matrixU.diagonal().setOnes(); if(ComputeV) m_matrixV.diagonal().setOnes(); } -sweep_again: - for(int p = 1; p < diagSize; ++p) + + bool finished = false; + while(!finished) { - for(int q = 0; q < p; ++q) + finished = true; + for(int p = 1; p < diagSize; ++p) { - if(std::max(ei_abs(work_matrix.coeff(p,q)),ei_abs(work_matrix.coeff(q,p))) - > std::max(ei_abs(work_matrix.coeff(p,p)),ei_abs(work_matrix.coeff(q,q)))*precision) + for(int q = 0; q < p; ++q) { - ei_svd_precondition_2x2_block_to_be_real::run(work_matrix, *this, p, q); + if(std::max(ei_abs(work_matrix.coeff(p,q)),ei_abs(work_matrix.coeff(q,p))) + > std::max(ei_abs(work_matrix.coeff(p,p)),ei_abs(work_matrix.coeff(q,q)))*precision) + { + finished = false; + ei_svd_precondition_2x2_block_to_be_real::run(work_matrix, *this, p, q); - PlanarRotation j_left, j_right; - ei_real_2x2_jacobi_svd(work_matrix, p, q, &j_left, &j_right); + PlanarRotation j_left, j_right; + ei_real_2x2_jacobi_svd(work_matrix, p, q, &j_left, &j_right); - work_matrix.applyOnTheLeft(p,q,j_left); - if(ComputeU) m_matrixU.applyOnTheRight(p,q,j_left.transpose()); + work_matrix.applyOnTheLeft(p,q,j_left); + if(ComputeU) m_matrixU.applyOnTheRight(p,q,j_left.transpose()); - work_matrix.applyOnTheRight(p,q,j_right); - if(ComputeV) m_matrixV.applyOnTheRight(p,q,j_right); + work_matrix.applyOnTheRight(p,q,j_right); + if(ComputeV) m_matrixV.applyOnTheRight(p,q,j_right); + } } } } - RealScalar biggestOnDiag = work_matrix.diagonal().cwise().abs().maxCoeff(); - RealScalar maxAllowedOffDiag = biggestOnDiag * precision; - for(int p = 0; p < diagSize; ++p) - { - for(int q = 0; q < p; ++q) - if(ei_abs(work_matrix.coeff(p,q)) > maxAllowedOffDiag) - goto sweep_again; - for(int q = p+1; q < diagSize; ++q) - if(ei_abs(work_matrix.coeff(p,q)) > maxAllowedOffDiag) - goto sweep_again; - } - for(int i = 0; i < diagSize; ++i) { RealScalar a = ei_abs(work_matrix.coeff(i,i)); diff --git a/test/jacobisvd.cpp b/test/jacobisvd.cpp index 8b4c7584e..5940b8497 100644 --- a/test/jacobisvd.cpp +++ b/test/jacobisvd.cpp @@ -27,7 +27,7 @@ #include #include -template void svd(const MatrixType& m, bool pickrandom = true) +template void svd(const MatrixType& m = MatrixType(), bool pickrandom = true) { int rows = m.rows(); int cols = m.cols(); @@ -48,7 +48,7 @@ template void svd(const MatrixType& m, bool pickrandom = tr if(pickrandom) a = MatrixType::Random(rows,cols); else a = m; - JacobiSVD svd(a); + JacobiSVD svd(a); MatrixType sigma = MatrixType::Zero(rows,cols); sigma.diagonal() = svd.singularValues().template cast(); MatrixUType u = svd.matrixU(); @@ -80,28 +80,27 @@ void test_jacobisvd() Matrix2cd m; m << 0, 1, 0, 1; - CALL_SUBTEST( svd(m, false) ); + CALL_SUBTEST(( svd(m, false) )); m << 1, 0, 1, 0; - CALL_SUBTEST( svd(m, false) ); + CALL_SUBTEST(( svd(m, false) )); Matrix2d n; n << 1, 1, 1, -1; - CALL_SUBTEST( svd(n, false) ); - CALL_SUBTEST( svd(Matrix3f()) ); - CALL_SUBTEST( svd(Matrix4d()) ); - CALL_SUBTEST( svd(MatrixXf(50,50)) ); - CALL_SUBTEST( svd(MatrixXcd(14,7)) ); - CALL_SUBTEST( svd(MatrixXd(10,50)) ); - - CALL_SUBTEST( svd(MatrixXcf(3,3)) ); - CALL_SUBTEST( svd(MatrixXd(30,30)) ); + CALL_SUBTEST(( svd(n, false) )); + CALL_SUBTEST(( svd() )); + CALL_SUBTEST(( svd() )); + CALL_SUBTEST(( svd , AtLeastAsManyColsAsRows>() )); + CALL_SUBTEST(( svd , AtLeastAsManyRowsAsCols>(Matrix(10,2)) )); + + CALL_SUBTEST(( svd(MatrixXf(50,50)) )); + CALL_SUBTEST(( svd(MatrixXcd(14,7)) )); } - CALL_SUBTEST( svd(MatrixXf(300,200)) ); - CALL_SUBTEST( svd(MatrixXcd(100,150)) ); + CALL_SUBTEST(( svd(MatrixXf(300,200)) )); + CALL_SUBTEST(( svd(MatrixXcd(100,150)) )); - CALL_SUBTEST( svd_verify_assert() ); - CALL_SUBTEST( svd_verify_assert() ); - CALL_SUBTEST( svd_verify_assert() ); - CALL_SUBTEST( svd_verify_assert() ); + CALL_SUBTEST(( svd_verify_assert() )); + CALL_SUBTEST(( svd_verify_assert() )); + CALL_SUBTEST(( svd_verify_assert() )); + CALL_SUBTEST(( svd_verify_assert() )); } -- cgit v1.2.3 From 2abd5eeffd61523ad87f6b44bcebdd083a5324b8 Mon Sep 17 00:00:00 2001 From: Hauke Heibel Date: Wed, 2 Sep 2009 20:57:41 +0200 Subject: Added support to overwrite the generator type. Eigen'fied the new variables. --- test/testsuite.cmake | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/test/testsuite.cmake b/test/testsuite.cmake index 6a44ce239..37ee87565 100644 --- a/test/testsuite.cmake +++ b/test/testsuite.cmake @@ -27,6 +27,13 @@ # - EIGEN_WORK_DIR: directory used to download the source files and make the builds # default: folder which contains this script # - EIGEN_CMAKE_ARGS: additional arguments passed to cmake +# - EIGEN_GENERATOR_TYPE: allows to overwrite the generator type +# default: nmake (windows +# See http://www.cmake.org/cmake/help/cmake2.6docs.html#section_Generators for a complete +# list of supported generators. +# - EIGEN_NO_UPDATE: allows to submit dash boards from local repositories +# This might be interesting in case you want to submit dashboards +# including local changes. # - CTEST_SOURCE_DIRECTORY: path to eigen's src (use a new and empty folder, not the one you are working on) # default: /src # - CTEST_BINARY_DIRECTORY: build directory @@ -132,11 +139,11 @@ endif(NOT EIGEN_MODE) ## mandatory variables (the default should be ok in most cases): -if(NOT IGNORE_CVS) +if(NOT EIGEN_NO_UPDATE) SET (CTEST_CVS_COMMAND "hg") SET (CTEST_CVS_CHECKOUT "${CTEST_CVS_COMMAND} clone http://bitbucket.org/eigen/eigen2 \"${CTEST_SOURCE_DIRECTORY}\"") SET(CTEST_BACKUP_AND_RESTORE TRUE) # the backup is CVS related ... -endif(NOT IGNORE_CVS) +endif(NOT EIGEN_NO_UPDATE) # which ctest command to use for running the dashboard SET (CTEST_COMMAND "${EIGEN_CMAKE_DIR}ctest -D ${EIGEN_MODE}") @@ -158,15 +165,24 @@ SET(CTEST_START_WITH_EMPTY_BINARY_DIRECTORY TRUE) # any quotes inside of this string if you use it if(WIN32 AND NOT UNIX) #message(SEND_ERROR "win32") - set(CTEST_CMAKE_COMMAND "${CTEST_CMAKE_COMMAND} -G \"NMake Makefiles\" -DCMAKE_MAKE_PROGRAM=nmake") - SET (CTEST_INITIAL_CACHE " - MAKECOMMAND:STRING=nmake -i - CMAKE_MAKE_PROGRAM:FILEPATH=nmake - CMAKE_GENERATOR:INTERNAL=NMake Makefiles - CMAKE_BUILD_TYPE:STRING=Release - BUILDNAME:STRING=${EIGEN_BUILD_STRING} - SITE:STRING=${EIGEN_SITE} - ") + if(EIGEN_GENERATOR_TYPE) + set(CTEST_CMAKE_COMMAND "${CTEST_CMAKE_COMMAND} -G \"${EIGEN_GENERATOR_TYPE}\"") + SET (CTEST_INITIAL_CACHE " + CMAKE_BUILD_TYPE:STRING=Release + BUILDNAME:STRING=${EIGEN_BUILD_STRING} + SITE:STRING=${EIGEN_SITE} + ") + else(EIGEN_GENERATOR_TYPE) + set(CTEST_CMAKE_COMMAND "${CTEST_CMAKE_COMMAND} -G \"NMake Makefiles\" -DCMAKE_MAKE_PROGRAM=nmake") + SET (CTEST_INITIAL_CACHE " + MAKECOMMAND:STRING=nmake -i + CMAKE_MAKE_PROGRAM:FILEPATH=nmake + CMAKE_GENERATOR:INTERNAL=NMake Makefiles + CMAKE_BUILD_TYPE:STRING=Release + BUILDNAME:STRING=${EIGEN_BUILD_STRING} + SITE:STRING=${EIGEN_SITE} + ") + endif(EIGEN_GENERATOR_TYPE) else(WIN32 AND NOT UNIX) SET (CTEST_INITIAL_CACHE " BUILDNAME:STRING=${EIGEN_BUILD_STRING} -- cgit v1.2.3 From e6c9d6c5285d0dd1ab8b72bb9ce9901d8078fc8f Mon Sep 17 00:00:00 2001 From: Hauke Heibel Date: Wed, 2 Sep 2009 20:59:57 +0200 Subject: Remove last lazyness warnings. --- test/product_notemporary.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/test/product_notemporary.cpp b/test/product_notemporary.cpp index 36a5a66ed..3b0329f43 100644 --- a/test/product_notemporary.cpp +++ b/test/product_notemporary.cpp @@ -69,7 +69,6 @@ template void product_notemporary(const MatrixType& m) r1 = ei_random(8,rows-r0); VERIFY_EVALUATION_COUNT( m3 = (m1 * m2.adjoint()), 1); - VERIFY_EVALUATION_COUNT( m3 = (m1 * m2.adjoint()).lazy(), 0); VERIFY_EVALUATION_COUNT( m3.noalias() = m1 * m2.adjoint(), 0); // NOTE in this case the slow product is used: @@ -79,16 +78,14 @@ template void product_notemporary(const MatrixType& m) VERIFY_EVALUATION_COUNT( m3.noalias() = s1 * m1 * s2 * m2.adjoint(), 0); VERIFY_EVALUATION_COUNT( m3.noalias() = s1 * m1 * s2 * (m1*s3+m2*s2).adjoint(), 1); VERIFY_EVALUATION_COUNT( m3.noalias() = (s1 * m1).adjoint() * s2 * m2, 0); - VERIFY_EVALUATION_COUNT( m3 += (s1 * (-m1*s3).adjoint() * (s2 * m2 * s3)).lazy(), 0); VERIFY_EVALUATION_COUNT( m3.noalias() += s1 * (-m1*s3).adjoint() * (s2 * m2 * s3), 0); - VERIFY_EVALUATION_COUNT( m3 -= (s1 * (m1.transpose() * m2)).lazy(), 0); VERIFY_EVALUATION_COUNT( m3.noalias() -= s1 * (m1.transpose() * m2), 0); VERIFY_EVALUATION_COUNT(( m3.block(r0,r0,r1,r1).noalias() += -m1.block(r0,c0,r1,c1) * (s2*m2.block(r0,c0,r1,c1)).adjoint() ), 0); VERIFY_EVALUATION_COUNT(( m3.block(r0,r0,r1,r1).noalias() -= s1 * m1.block(r0,c0,r1,c1) * m2.block(c0,r0,c1,r1) ), 0); // NOTE this is because the Block expression is not handled yet by our expression analyser - VERIFY_EVALUATION_COUNT(( m3.block(r0,r0,r1,r1) = (s1 * m1.block(r0,c0,r1,c1) * (s1*m2).block(c0,r0,c1,r1)).lazy() ), 1); + VERIFY_EVALUATION_COUNT(( m3.block(r0,r0,r1,r1).noalias() = s1 * m1.block(r0,c0,r1,c1) * (s1*m2).block(c0,r0,c1,r1) ), 1); VERIFY_EVALUATION_COUNT( m3.noalias() -= (s1 * m1).template triangularView() * m2, 0); VERIFY_EVALUATION_COUNT( rm3.noalias() = (s1 * m1.adjoint()).template triangularView() * (m2+m2), 1); -- cgit v1.2.3 From 8d449bd80e6e34506dae1be31478dc46a87f663b Mon Sep 17 00:00:00 2001 From: Hauke Heibel Date: Wed, 2 Sep 2009 21:23:09 +0200 Subject: Removed debug cout. Disabled MSVC inconsistent DLL linkage. --- Eigen/QtAlignedMalloc | 6 +++++- Eigen/src/Core/util/DisableMSVCWarnings.h | 3 ++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Eigen/QtAlignedMalloc b/Eigen/QtAlignedMalloc index de4da752c..fce7edf9b 100644 --- a/Eigen/QtAlignedMalloc +++ b/Eigen/QtAlignedMalloc @@ -6,8 +6,10 @@ #if (!EIGEN_MALLOC_ALREADY_ALIGNED) +#include "src/Core/util/DisableMSVCWarnings.h" + void *qMalloc(size_t size) -{std::cerr << "ok\n"; +{ return Eigen::ei_aligned_malloc(size); } @@ -24,6 +26,8 @@ void *qRealloc(void *ptr, size_t size) return newPtr; } +#include "src/Core/util/EnableMSVCWarnings.h" + #endif #endif // EIGEN_QTMALLOC_MODULE_H diff --git a/Eigen/src/Core/util/DisableMSVCWarnings.h b/Eigen/src/Core/util/DisableMSVCWarnings.h index ca8e245b3..c08d0389f 100644 --- a/Eigen/src/Core/util/DisableMSVCWarnings.h +++ b/Eigen/src/Core/util/DisableMSVCWarnings.h @@ -1,5 +1,6 @@ #ifdef _MSC_VER + // 4273 - QtAlignedMalloc, inconsistent dll linkage #pragma warning( push ) - #pragma warning( disable : 4181 4244 4127 4211 4522 4717 ) + #pragma warning( disable : 4181 4244 4127 4211 4273 4522 4717 ) #endif -- cgit v1.2.3 From c893917d65724aee15cad8c2d5d711d991c361c9 Mon Sep 17 00:00:00 2001 From: Gael Guennebaud Date: Thu, 3 Sep 2009 10:45:32 +0200 Subject: Fix serious bug discovered with gcc 4.2 --- Eigen/src/Core/Product.h | 12 ++++++------ Eigen/src/Core/SolveTriangular.h | 4 ++-- Eigen/src/Core/products/GeneralMatrixMatrix.h | 6 +++--- Eigen/src/Core/products/SelfadjointMatrixVector.h | 4 ++-- Eigen/src/Core/products/SelfadjointProduct.h | 2 +- Eigen/src/Core/products/SelfadjointRank2Update.h | 2 +- Eigen/src/Core/products/TriangularMatrixVector.h | 2 +- 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Eigen/src/Core/Product.h b/Eigen/src/Core/Product.h index f4c8af6ea..5c6cee426 100644 --- a/Eigen/src/Core/Product.h +++ b/Eigen/src/Core/Product.h @@ -63,7 +63,7 @@ template struct ei_product_type Cols = Rhs::ColsAtCompileTime, Depth = EIGEN_ENUM_MIN(Lhs::ColsAtCompileTime,Rhs::RowsAtCompileTime) }; - + // the splitting into different lines of code here, introducing the _select enums and the typedef below, // is to work around an internal compiler error with gcc 4.1 and 4.2. private: @@ -73,7 +73,7 @@ private: depth_select = Depth>=EIGEN_CACHEFRIENDLY_PRODUCT_THRESHOLD ? Large : (Depth==1 ? 1 : Small) }; typedef ei_product_type_selector product_type_selector; - + public: enum { value = product_type_selector::ret @@ -144,7 +144,7 @@ struct ProductReturnType ***********************************************************************/ // FIXME : maybe the "inner product" could return a Scalar -// instead of a 1x1 matrix ?? +// instead of a 1x1 matrix ?? // Pro: more natural for the user // Cons: this could be a problem if in a meta unrolled algorithm a matrix-matrix // product ends up to a row-vector times col-vector product... To tackle this use @@ -201,7 +201,7 @@ class GeneralProduct template void scaleAndAddTo(Dest& dest, Scalar alpha) const { - ei_outer_product_selector::run(*this, dest, alpha); + ei_outer_product_selector<(int(Dest::Flags)&RowMajorBit) ? RowMajor : ColMajor>::run(*this, dest, alpha); } }; @@ -259,7 +259,7 @@ class GeneralProduct template void scaleAndAddTo(Dest& dst, Scalar alpha) const { ei_assert(m_lhs.rows() == dst.rows() && m_rhs.cols() == dst.cols()); - ei_gemv_selector::ActualAccess)>::run(*this, dst, alpha); } }; @@ -339,7 +339,7 @@ template<> struct ei_gemv_selector Scalar actualAlpha = alpha * LhsBlasTraits::extractScalarFactor(prod.lhs()) * RhsBlasTraits::extractScalarFactor(prod.rhs()); - + enum { DirectlyUseRhs = ((ei_packet_traits::size==1) || (_ActualRhsType::Flags&ActualPacketAccessBit)) && (!(_ActualRhsType::Flags & RowMajorBit)) diff --git a/Eigen/src/Core/SolveTriangular.h b/Eigen/src/Core/SolveTriangular.h index 810b08240..c7f0cd227 100644 --- a/Eigen/src/Core/SolveTriangular.h +++ b/Eigen/src/Core/SolveTriangular.h @@ -30,7 +30,7 @@ template struct ei_triangular_solver_selector; @@ -157,7 +157,7 @@ struct ei_triangular_solver_selector + (Rhs::Flags&RowMajorBit) ? RowMajor : ColMajor> ::run(lhs.rows(), Side==OnTheLeft? rhs.cols() : rhs.rows(), &actualLhs.coeff(0,0), actualLhs.stride(), &rhs.coeffRef(0,0), rhs.stride()); } }; diff --git a/Eigen/src/Core/products/GeneralMatrixMatrix.h b/Eigen/src/Core/products/GeneralMatrixMatrix.h index 8b3b13266..c4692b872 100644 --- a/Eigen/src/Core/products/GeneralMatrixMatrix.h +++ b/Eigen/src/Core/products/GeneralMatrixMatrix.h @@ -149,9 +149,9 @@ class GeneralProduct ei_general_matrix_matrix_product< Scalar, - (_ActualLhsType::Flags&RowMajorBit)?RowMajor:ColMajor, bool(LhsBlasTraits::NeedToConjugate), - (_ActualRhsType::Flags&RowMajorBit)?RowMajor:ColMajor, bool(RhsBlasTraits::NeedToConjugate), - (Dest::Flags&RowMajorBit)?RowMajor:ColMajor> + (_ActualLhsType::Flags&RowMajorBit) ? RowMajor : ColMajor, bool(LhsBlasTraits::NeedToConjugate), + (_ActualRhsType::Flags&RowMajorBit) ? RowMajor : ColMajor, bool(RhsBlasTraits::NeedToConjugate), + (Dest::Flags&RowMajorBit) ? RowMajor : ColMajor> ::run( this->rows(), this->cols(), lhs.cols(), (const Scalar*)&(lhs.const_cast_derived().coeffRef(0,0)), lhs.stride(), diff --git a/Eigen/src/Core/products/SelfadjointMatrixVector.h b/Eigen/src/Core/products/SelfadjointMatrixVector.h index d5927307d..c27454bee 100644 --- a/Eigen/src/Core/products/SelfadjointMatrixVector.h +++ b/Eigen/src/Core/products/SelfadjointMatrixVector.h @@ -41,7 +41,7 @@ static EIGEN_DONT_INLINE void ei_product_selfadjoint_vector( const int PacketSize = sizeof(Packet)/sizeof(Scalar); enum { - IsRowMajor = StorageOrder==RowMajorBit ? 1 : 0, + IsRowMajor = StorageOrder==RowMajor ? 1 : 0, IsLower = UpLo == LowerTriangularBit ? 1 : 0, FirstTriangular = IsRowMajor == IsLower }; @@ -186,7 +186,7 @@ struct SelfadjointProductMatrix * RhsBlasTraits::extractScalarFactor(m_rhs); ei_assert(dst.stride()==1 && "not implemented yet"); - ei_product_selfadjoint_vector::Flags&RowMajorBit, int(LhsUpLo), bool(LhsBlasTraits::NeedToConjugate), bool(RhsBlasTraits::NeedToConjugate)> + ei_product_selfadjoint_vector::Flags&RowMajorBit) ? RowMajor : ColMajor, int(LhsUpLo), bool(LhsBlasTraits::NeedToConjugate), bool(RhsBlasTraits::NeedToConjugate)> ( lhs.rows(), // size &lhs.coeff(0,0), lhs.stride(), // lhs info diff --git a/Eigen/src/Core/products/SelfadjointProduct.h b/Eigen/src/Core/products/SelfadjointProduct.h index d3fc962d9..f9cdda637 100644 --- a/Eigen/src/Core/products/SelfadjointProduct.h +++ b/Eigen/src/Core/products/SelfadjointProduct.h @@ -132,7 +132,7 @@ SelfAdjointView& SelfAdjointView Scalar actualAlpha = alpha * UBlasTraits::extractScalarFactor(u.derived()); - enum { IsRowMajor = (ei_traits::Flags&RowMajorBit)?1:0 }; + enum { IsRowMajor = (ei_traits::Flags&RowMajorBit) ? 1 : 0 }; ei_selfadjoint_product& SelfAdjointView Scalar actualAlpha = alpha * UBlasTraits::extractScalarFactor(u.derived()) * VBlasTraits::extractScalarFactor(v.derived()); - enum { IsRowMajor = (ei_traits::Flags&RowMajorBit)?1:0 }; + enum { IsRowMajor = (ei_traits::Flags&RowMajorBit) ? 1 : 0 }; ei_selfadjoint_rank2_update_selector::ret>::type, typename ei_cleantype::ret>::type, diff --git a/Eigen/src/Core/products/TriangularMatrixVector.h b/Eigen/src/Core/products/TriangularMatrixVector.h index 620b090b9..291177445 100644 --- a/Eigen/src/Core/products/TriangularMatrixVector.h +++ b/Eigen/src/Core/products/TriangularMatrixVector.h @@ -145,7 +145,7 @@ struct TriangularProduct Mode, LhsBlasTraits::NeedToConjugate, RhsBlasTraits::NeedToConjugate, - ei_traits::Flags&RowMajorBit> + (int(ei_traits::Flags)&RowMajorBit) ? RowMajor : ColMajor> ::run(lhs,rhs,dst,actualAlpha); } }; -- cgit v1.2.3 From 16c7b1daabd2697950605706a8bc68ae32d70fc8 Mon Sep 17 00:00:00 2001 From: Gael Guennebaud Date: Thu, 3 Sep 2009 11:17:16 +0200 Subject: add examples for makeJacobi and makeGivens --- Eigen/Jacobi | 6 +++++- Eigen/src/Jacobi/Jacobi.h | 39 ++++++++++++++++++++++++++----------- doc/Doxyfile.in | 1 + doc/snippets/Jacobi_makeGivens.cpp | 6 ++++++ doc/snippets/Jacobi_makeJacobi.cpp | 8 ++++++++ doc/snippets/compile_snippet.cpp.in | 1 + 6 files changed, 49 insertions(+), 12 deletions(-) create mode 100644 doc/snippets/Jacobi_makeGivens.cpp create mode 100644 doc/snippets/Jacobi_makeJacobi.cpp diff --git a/Eigen/Jacobi b/Eigen/Jacobi index 33a6b757e..da67d2a6a 100644 --- a/Eigen/Jacobi +++ b/Eigen/Jacobi @@ -8,11 +8,15 @@ namespace Eigen { /** \defgroup Jacobi_Module Jacobi module - * This module provides Jacobi rotations. + * This module provides Jacobi and Givens rotations. * * \code * #include * \endcode + * + * In addition to listed classes, it defines the two following MatrixBase methods to apply a Jacobi or Givens rotation: + * - MatrixBase::applyOnTheLeft() + * - MatrixBase::applyOnTheRight(). */ #include "src/Jacobi/Jacobi.h" diff --git a/Eigen/src/Jacobi/Jacobi.h b/Eigen/src/Jacobi/Jacobi.h index 66ba06b19..3e3cce665 100644 --- a/Eigen/src/Jacobi/Jacobi.h +++ b/Eigen/src/Jacobi/Jacobi.h @@ -26,16 +26,23 @@ #ifndef EIGEN_JACOBI_H #define EIGEN_JACOBI_H -/** \ingroup Jacobi +/** \ingroup Jacobi_Module + * \jacobi_module * \class PlanarRotation * \brief Represents a rotation in the plane from a cosine-sine pair. * * This class represents a Jacobi or Givens rotation. - * This is a 2D clock-wise rotation in the plane \c J of angle \f$ \theta \f$ defined by + * This is a 2D rotation in the plane \c J of angle \f$ \theta \f$ defined by * its cosine \c c and sine \c s as follow: * \f$ J = \left ( \begin{array}{cc} c & \overline s \\ -s & \overline c \end{array} \right ) \f$ * - * \sa MatrixBase::makeJacobi(), MatrixBase::applyOnTheLeft(), MatrixBase::applyOnTheRight() + * You can apply the respective counter-clockwise rotation to a column vector \c v by + * applying its adjoint on the left: \f$ v = J^* v \f$ that translates to the following Eigen code: + * \code + * v.applyOnTheLeft(J.adjoint()); + * \endcode + * + * \sa MatrixBase::applyOnTheLeft(), MatrixBase::applyOnTheRight() */ template class PlanarRotation { @@ -79,11 +86,10 @@ template class PlanarRotation Scalar m_c, m_s; }; -/** Makes \c *this as a Jacobi rotation \a J such that applying \a J on both the right and left sides of the 2x2 matrix - * \f$ B = \left ( \begin{array}{cc} x & y \\ * & z \end{array} \right )\f$ yields - * a diagonal matrix \f$ A = J^* B J \f$ +/** Makes \c *this as a Jacobi rotation \a J such that applying \a J on both the right and left sides of the selfadjoint 2x2 matrix + * \f$ B = \left ( \begin{array}{cc} x & y \\ * & z \end{array} \right )\f$ yields a diagonal matrix \f$ A = J^* B J \f$ * - * \sa MatrixBase::makeJacobi(), MatrixBase::applyOnTheLeft(), MatrixBase::applyOnTheRight() + * \sa MatrixBase::makeJacobi(const MatrixBase&, int, int), MatrixBase::applyOnTheLeft(), MatrixBase::applyOnTheRight() */ template bool PlanarRotation::makeJacobi(RealScalar x, Scalar y, RealScalar z) @@ -116,10 +122,13 @@ bool PlanarRotation::makeJacobi(RealScalar x, Scalar y, RealScalar z) } } -/** Makes \c *this as a Jacobi rotation \c J such that applying \a J on both the right and left sides of the 2x2 matrix +/** Makes \c *this as a Jacobi rotation \c J such that applying \a J on both the right and left sides of the 2x2 selfadjoint matrix * \f$ B = \left ( \begin{array}{cc} \text{this}_{pp} & \text{this}_{pq} \\ * & \text{this}_{qq} \end{array} \right )\f$ yields * a diagonal matrix \f$ A = J^* B J \f$ * + * Example: \include Jacobi_makeJacobi.cpp + * Output: \verbinclude Jacobi_makeJacobi.out + * * \sa PlanarRotation::makeJacobi(RealScalar, Scalar, RealScalar), MatrixBase::applyOnTheLeft(), MatrixBase::applyOnTheRight() */ template @@ -136,6 +145,9 @@ inline bool PlanarRotation::makeJacobi(const MatrixBase& m, int * The value of \a z is returned if \a z is not null (the default is null). * Also note that G is built such that the cosine is always real. * + * Example: \include Jacobi_makeGivens.cpp + * Output: \verbinclude Jacobi_makeGivens.out + * * \sa MatrixBase::applyOnTheLeft(), MatrixBase::applyOnTheRight() */ template @@ -171,9 +183,11 @@ void PlanarRotation::makeGivens(const Scalar& p, const Scalar& q, Scalar } // specialization for reals +// TODO compute z template void PlanarRotation::makeGivens(const Scalar& p, const Scalar& q, Scalar* z, ei_meta_false) { + ei_assert(z==0 && "not implemented yet"); // from Golub's "Matrix Computations", algorithm 5.1.3 if(q==0) { @@ -197,7 +211,8 @@ void PlanarRotation::makeGivens(const Scalar& p, const Scalar& q, Scalar * Implementation of MatrixBase methods ****************************************************************************************/ -/** Applies the clock wise 2D rotation \a j to the set of 2D vectors of cordinates \a x and \a y: +/** \jacobi_module + * Applies the clock wise 2D rotation \a j to the set of 2D vectors of cordinates \a x and \a y: * \f$ \left ( \begin{array}{cc} x \\ y \end{array} \right ) = J \left ( \begin{array}{cc} x \\ y \end{array} \right ) \f$ * * \sa MatrixBase::applyOnTheLeft(), MatrixBase::applyOnTheRight() @@ -205,7 +220,8 @@ void PlanarRotation::makeGivens(const Scalar& p, const Scalar& q, Scalar template void ei_apply_rotation_in_the_plane(VectorX& _x, VectorY& _y, const PlanarRotation& j); -/** Applies the rotation in the plane \a j to the rows \a p and \a q of \c *this, i.e., it computes B = J * B, +/** \jacobi_module + * Applies the rotation in the plane \a j to the rows \a p and \a q of \c *this, i.e., it computes B = J * B, * with \f$ B = \left ( \begin{array}{cc} \text{*this.row}(p) \\ \text{*this.row}(q) \end{array} \right ) \f$. * * \sa class PlanarRotation, MatrixBase::applyOnTheRight(), ei_apply_rotation_in_the_plane() @@ -219,7 +235,8 @@ inline void MatrixBase::applyOnTheLeft(int p, int q, const PlanarRotati ei_apply_rotation_in_the_plane(x, y, j); } -/** Applies the rotation in the plane \a j to the columns \a p and \a q of \c *this, i.e., it computes B = B * J +/** \ingroup Jacobi_Module + * Applies the rotation in the plane \a j to the columns \a p and \a q of \c *this, i.e., it computes B = B * J * with \f$ B = \left ( \begin{array}{cc} \text{*this.col}(p) & \text{*this.col}(q) \end{array} \right ) \f$. * * \sa class PlanarRotation, MatrixBase::applyOnTheLeft(), ei_apply_rotation_in_the_plane() diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in index cd67bb07d..a66750c3e 100644 --- a/doc/Doxyfile.in +++ b/doc/Doxyfile.in @@ -208,6 +208,7 @@ ALIASES = "only_for_vectors=This is only for vectors (either row- "lu_module=This is defined in the %LU module. \code #include \endcode" \ "cholesky_module=This is defined in the %Cholesky module. \code #include \endcode" \ "qr_module=This is defined in the %QR module. \code #include \endcode" \ + "jacobi_module=This is defined in the %Jacobi module. \code #include \endcode" \ "svd_module=This is defined in the %SVD module. \code #include \endcode" \ "geometry_module=This is defined in the %Geometry module. \code #include \endcode" \ "leastsquares_module=This is defined in the %LeastSquares module. \code #include \endcode" \ diff --git a/doc/snippets/Jacobi_makeGivens.cpp b/doc/snippets/Jacobi_makeGivens.cpp new file mode 100644 index 000000000..3a4defe24 --- /dev/null +++ b/doc/snippets/Jacobi_makeGivens.cpp @@ -0,0 +1,6 @@ +Vector2f v = Vector2f::Random(); +PlanarRotation G; +G.makeGivens(v.x(), v.y()); +cout << "Here is the vector v:" << endl << v << endl; +v.applyOnTheLeft(0, 1, G.adjoint()); +cout << "Here is the vector J' * v:" << endl << v << endl; \ No newline at end of file diff --git a/doc/snippets/Jacobi_makeJacobi.cpp b/doc/snippets/Jacobi_makeJacobi.cpp new file mode 100644 index 000000000..5c0ab7374 --- /dev/null +++ b/doc/snippets/Jacobi_makeJacobi.cpp @@ -0,0 +1,8 @@ +Matrix2f m = Matrix2f::Random(); +m = (m + m.adjoint()).eval(); +PlanarRotation J; +J.makeJacobi(m, 0, 1); +cout << "Here is the matrix m:" << endl << m << endl; +m.applyOnTheLeft(0, 1, J.adjoint()); +m.applyOnTheRight(0, 1, J); +cout << "Here is the matrix J' * m * J:" << endl << m << endl; \ No newline at end of file diff --git a/doc/snippets/compile_snippet.cpp.in b/doc/snippets/compile_snippet.cpp.in index d074cac50..3bea1ac8d 100644 --- a/doc/snippets/compile_snippet.cpp.in +++ b/doc/snippets/compile_snippet.cpp.in @@ -4,6 +4,7 @@ #include #include #include +#include using namespace Eigen; using namespace std; -- cgit v1.2.3 From 9515b00876ab8e84ae4beb61e8661400ebb49522 Mon Sep 17 00:00:00 2001 From: Gael Guennebaud Date: Thu, 3 Sep 2009 11:22:42 +0200 Subject: remove the \addexample tags --- Eigen/src/Array/BooleanRedux.h | 8 +++----- Eigen/src/Array/Random.h | 10 ++++------ Eigen/src/Core/Block.h | 10 ---------- Eigen/src/Core/CommaInitializer.h | 2 -- Eigen/src/Core/CwiseBinaryOp.h | 2 -- Eigen/src/Core/CwiseNullaryOp.h | 6 ------ Eigen/src/Core/CwiseUnaryOp.h | 2 -- Eigen/src/Core/CwiseUnaryView.h | 2 -- Eigen/src/Core/DiagonalMatrix.h | 26 ++++++++++++-------------- Eigen/src/Core/VectorBlock.h | 12 ++---------- Eigen/src/Geometry/AngleAxis.h | 4 +--- doc/Doxyfile.in | 1 - unsupported/Eigen/BVH | 2 -- unsupported/doc/Doxyfile.in | 1 - 14 files changed, 22 insertions(+), 66 deletions(-) diff --git a/Eigen/src/Array/BooleanRedux.h b/Eigen/src/Array/BooleanRedux.h index 6a5e208de..ab6e06d56 100644 --- a/Eigen/src/Array/BooleanRedux.h +++ b/Eigen/src/Array/BooleanRedux.h @@ -78,10 +78,8 @@ struct ei_any_unroller }; /** \array_module - * - * \returns true if all coefficients are true * - * \addexample CwiseAll \label How to check whether a point is inside a box (using operator< and all()) + * \returns true if all coefficients are true * * Example: \include MatrixBase_all.cpp * Output: \verbinclude MatrixBase_all.out @@ -107,7 +105,7 @@ inline bool MatrixBase::all() const } /** \array_module - * + * * \returns true if at least one coefficient is true * * \sa MatrixBase::all() @@ -131,7 +129,7 @@ inline bool MatrixBase::any() const } /** \array_module - * + * * \returns the number of coefficients which evaluate to true * * \sa MatrixBase::all(), MatrixBase::any() diff --git a/Eigen/src/Array/Random.h b/Eigen/src/Array/Random.h index 15cc6ae7c..adadf99e3 100644 --- a/Eigen/src/Array/Random.h +++ b/Eigen/src/Array/Random.h @@ -34,7 +34,7 @@ struct ei_functor_traits > { enum { Cost = 5 * NumTraits::MulCost, PacketAccess = false, IsRepeatable = false }; }; /** \array_module - * + * * \returns a random matrix expression * * The parameters \a rows and \a cols are the number of rows and of columns of @@ -44,8 +44,6 @@ struct ei_functor_traits > * it is redundant to pass \a rows and \a cols as arguments, so Random() should be used * instead. * - * \addexample RandomExample \label How to create a matrix with random coefficients - * * Example: \include MatrixBase_random_int_int.cpp * Output: \verbinclude MatrixBase_random_int_int.out * @@ -63,7 +61,7 @@ MatrixBase::Random(int rows, int cols) } /** \array_module - * + * * \returns a random vector expression * * The parameter \a size is the size of the returned vector. @@ -92,7 +90,7 @@ MatrixBase::Random(int size) } /** \array_module - * + * * \returns a fixed-size random matrix or vector expression * * This variant is only for fixed-size MatrixBase types. For dynamic-size types, you @@ -115,7 +113,7 @@ MatrixBase::Random() } /** \array_module - * + * * Sets all coefficients in this expression to random values. * * Example: \include MatrixBase_setRandom.cpp diff --git a/Eigen/src/Core/Block.h b/Eigen/src/Core/Block.h index 42d6bc3c3..cebfeaf75 100644 --- a/Eigen/src/Core/Block.h +++ b/Eigen/src/Core/Block.h @@ -308,8 +308,6 @@ class Block * \param blockRows the number of rows in the block * \param blockCols the number of columns in the block * - * \addexample BlockIntIntIntInt \label How to reference a sub-matrix (dynamic-size) - * * Example: \include MatrixBase_block_int_int_int_int.cpp * Output: \verbinclude MatrixBase_block_int_int_int_int.out * @@ -341,8 +339,6 @@ inline const typename BlockReturnType::Type MatrixBase * \param cRows the number of rows in the corner * \param cCols the number of columns in the corner * - * \addexample BlockCornerDynamicSize \label How to reference a sub-corner of a matrix - * * Example: \include MatrixBase_corner_enum_int_int.cpp * Output: \verbinclude MatrixBase_corner_enum_int_int.out * @@ -452,8 +448,6 @@ MatrixBase::corner(CornerType type) const * \param startRow the first row in the block * \param startCol the first column in the block * - * \addexample BlockSubMatrixFixedSize \label How to reference a sub-matrix (fixed-size) - * * Example: \include MatrixBase_block_int_int.cpp * Output: \verbinclude MatrixBase_block_int_int.out * @@ -480,8 +474,6 @@ MatrixBase::block(int startRow, int startCol) const } /** \returns an expression of the \a i-th column of *this. Note that the numbering starts at 0. - * - * \addexample BlockColumn \label How to reference a single column of a matrix * * Example: \include MatrixBase_col.cpp * Output: \verbinclude MatrixBase_col.out @@ -503,8 +495,6 @@ MatrixBase::col(int i) const } /** \returns an expression of the \a i-th row of *this. Note that the numbering starts at 0. - * - * \addexample BlockRow \label How to reference a single row of a matrix * * Example: \include MatrixBase_row.cpp * Output: \verbinclude MatrixBase_row.out diff --git a/Eigen/src/Core/CommaInitializer.h b/Eigen/src/Core/CommaInitializer.h index c0dd701f2..e86f47ad0 100644 --- a/Eigen/src/Core/CommaInitializer.h +++ b/Eigen/src/Core/CommaInitializer.h @@ -124,8 +124,6 @@ struct CommaInitializer * The coefficients must be provided in a row major order and exactly match * the size of the matrix. Otherwise an assertion is raised. * - * \addexample CommaInit \label How to easily set all the coefficients of a matrix - * * Example: \include MatrixBase_set.cpp * Output: \verbinclude MatrixBase_set.out * diff --git a/Eigen/src/Core/CwiseBinaryOp.h b/Eigen/src/Core/CwiseBinaryOp.h index 78de9fbce..875bc9aa5 100644 --- a/Eigen/src/Core/CwiseBinaryOp.h +++ b/Eigen/src/Core/CwiseBinaryOp.h @@ -291,8 +291,6 @@ Cwise::max(const MatrixBase &other) const * The template parameter \a CustomBinaryOp is the type of the functor * of the custom operator (see class CwiseBinaryOp for an example) * - * \addexample CustomCwiseBinaryFunctors \label How to use custom coeff wise binary functors - * * Here is an example illustrating the use of custom functors: * \include class_CwiseBinaryOp.cpp * Output: \verbinclude class_CwiseBinaryOp.out diff --git a/Eigen/src/Core/CwiseNullaryOp.h b/Eigen/src/Core/CwiseNullaryOp.h index 804bc2e74..61ce51885 100644 --- a/Eigen/src/Core/CwiseNullaryOp.h +++ b/Eigen/src/Core/CwiseNullaryOp.h @@ -317,8 +317,6 @@ Matrix<_Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols>::setConstant(int row * it is redundant to pass \a rows and \a cols as arguments, so Zero() should be used * instead. * - * \addexample Zero \label How to take get a zero matrix - * * Example: \include MatrixBase_zero_int_int.cpp * Output: \verbinclude MatrixBase_zero_int_int.out * @@ -448,8 +446,6 @@ Matrix<_Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols>::setZero(int rows, i * it is redundant to pass \a rows and \a cols as arguments, so Ones() should be used * instead. * - * \addexample One \label How to get a matrix with all coefficients equal one - * * Example: \include MatrixBase_ones_int_int.cpp * Output: \verbinclude MatrixBase_ones_int_int.out * @@ -576,8 +572,6 @@ Matrix<_Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols>::setOnes(int rows, i * it is redundant to pass \a rows and \a cols as arguments, so Identity() should be used * instead. * - * \addexample Identity \label How to get an identity matrix - * * Example: \include MatrixBase_identity_int_int.cpp * Output: \verbinclude MatrixBase_identity_int_int.out * diff --git a/Eigen/src/Core/CwiseUnaryOp.h b/Eigen/src/Core/CwiseUnaryOp.h index d701c25d9..6e4c0d4ec 100644 --- a/Eigen/src/Core/CwiseUnaryOp.h +++ b/Eigen/src/Core/CwiseUnaryOp.h @@ -109,8 +109,6 @@ class CwiseUnaryOp : ei_no_assignment_operator, * The template parameter \a CustomUnaryOp is the type of the functor * of the custom unary operator. * - * \addexample CustomCwiseUnaryFunctors \label How to use custom coeff wise unary functors - * * Example: * \include class_CwiseUnaryOp.cpp * Output: \verbinclude class_CwiseUnaryOp.out diff --git a/Eigen/src/Core/CwiseUnaryView.h b/Eigen/src/Core/CwiseUnaryView.h index 4b7d87953..580344379 100644 --- a/Eigen/src/Core/CwiseUnaryView.h +++ b/Eigen/src/Core/CwiseUnaryView.h @@ -98,8 +98,6 @@ class CwiseUnaryView : public MatrixBase > * The template parameter \a CustomUnaryOp is the type of the functor * of the custom unary operator. * - * \addexample CustomCwiseUnaryFunctors \label How to use custom coeff wise unary functors - * * Example: * \include class_CwiseUnaryOp.cpp * Output: \verbinclude class_CwiseUnaryOp.out diff --git a/Eigen/src/Core/DiagonalMatrix.h b/Eigen/src/Core/DiagonalMatrix.h index ebbed15d4..a74695921 100644 --- a/Eigen/src/Core/DiagonalMatrix.h +++ b/Eigen/src/Core/DiagonalMatrix.h @@ -32,7 +32,7 @@ class DiagonalBase : public AnyMatrixBase public: typedef typename ei_traits::DiagonalVectorType DiagonalVectorType; typedef typename DiagonalVectorType::Scalar Scalar; - + enum { RowsAtCompileTime = DiagonalVectorType::SizeAtCompileTime, ColsAtCompileTime = DiagonalVectorType::SizeAtCompileTime, @@ -41,14 +41,14 @@ class DiagonalBase : public AnyMatrixBase IsVectorAtCompileTime = 0, Flags = 0 }; - + typedef Matrix DenseMatrixType; - + #ifndef EIGEN_PARSED_BY_DOXYGEN inline const Derived& derived() const { return *static_cast(this); } inline Derived& derived() { return *static_cast(this); } #endif // not EIGEN_PARSED_BY_DOXYGEN - + DenseMatrixType toDenseMatrix() const { return derived(); } template void evalToDense(MatrixBase &other) const; @@ -64,7 +64,7 @@ class DiagonalBase : public AnyMatrixBase inline int rows() const { return diagonal().size(); } inline int cols() const { return diagonal().size(); } - + template const DiagonalProduct operator*(const MatrixBase &matrix) const; @@ -100,20 +100,20 @@ class DiagonalMatrix : public DiagonalBase > { public: - + typedef typename ei_traits::DiagonalVectorType DiagonalVectorType; typedef const DiagonalMatrix& Nested; typedef _Scalar Scalar; - + protected: DiagonalVectorType m_diagonal; - + public: inline const DiagonalVectorType& diagonal() const { return m_diagonal; } inline DiagonalVectorType& diagonal() { return m_diagonal; } - + /** Default constructor without initialization */ inline DiagonalMatrix() {} @@ -152,7 +152,7 @@ class DiagonalMatrix m_diagonal = other.m_diagonal(); return *this; } - + inline void resize(int size) { m_diagonal.resize(size); } inline void setZero() { m_diagonal.setZero(); } inline void setZero(int size) { m_diagonal.setZero(size); } @@ -194,10 +194,10 @@ class DiagonalWrapper public: typedef _DiagonalVectorType DiagonalVectorType; typedef DiagonalWrapper Nested; - + inline DiagonalWrapper(const DiagonalVectorType& diagonal) : m_diagonal(diagonal) {} const DiagonalVectorType& diagonal() const { return m_diagonal; } - + protected: const typename DiagonalVectorType::Nested m_diagonal; }; @@ -207,8 +207,6 @@ class DiagonalWrapper * * \only_for_vectors * - * \addexample AsDiagonalExample \label How to build a diagonal matrix from a vector - * * Example: \include MatrixBase_asDiagonal.cpp * Output: \verbinclude MatrixBase_asDiagonal.out * diff --git a/Eigen/src/Core/VectorBlock.h b/Eigen/src/Core/VectorBlock.h index 7ce5977f6..b291f7b1a 100644 --- a/Eigen/src/Core/VectorBlock.h +++ b/Eigen/src/Core/VectorBlock.h @@ -88,7 +88,7 @@ template class VectorBlock using Base::operator-=; using Base::operator*=; using Base::operator/=; - + /** Dynamic-size constructor */ inline VectorBlock(const VectorType& vector, int start, int size) @@ -96,7 +96,7 @@ template class VectorBlock IsColVector ? start : 0, IsColVector ? 0 : start, IsColVector ? size : 1, IsColVector ? 1 : size) { - + EIGEN_STATIC_ASSERT_VECTOR_ONLY(VectorBlock); } @@ -114,8 +114,6 @@ template class VectorBlock * * \only_for_vectors * - * \addexample VectorBlockIntInt \label How to reference a sub-vector (dynamic size) - * * \param start the first coefficient in the segment * \param size the number of coefficients in the segment * @@ -151,8 +149,6 @@ MatrixBase::segment(int start, int size) const * * \param size the number of coefficients in the block * - * \addexample BlockInt \label How to reference a sub-vector (fixed-size) - * * Example: \include MatrixBase_start_int.cpp * Output: \verbinclude MatrixBase_start_int.out * @@ -185,8 +181,6 @@ MatrixBase::start(int size) const * * \param size the number of coefficients in the block * - * \addexample BlockEnd \label How to reference the end of a vector (fixed-size) - * * Example: \include MatrixBase_end_int.cpp * Output: \verbinclude MatrixBase_end_int.out * @@ -251,8 +245,6 @@ MatrixBase::segment(int start) const * * The template parameter \a Size is the number of coefficients in the block * - * \addexample BlockStart \label How to reference the start of a vector (fixed-size) - * * Example: \include MatrixBase_template_int_start.cpp * Output: \verbinclude MatrixBase_template_int_start.out * diff --git a/Eigen/src/Geometry/AngleAxis.h b/Eigen/src/Geometry/AngleAxis.h index 9fd239354..b9dfa6972 100644 --- a/Eigen/src/Geometry/AngleAxis.h +++ b/Eigen/src/Geometry/AngleAxis.h @@ -39,8 +39,6 @@ * \li \c AngleAxisf for \c float * \li \c AngleAxisd for \c double * - * \addexample AngleAxisForEuler \label How to define a rotation from Euler-angles - * * Combined with MatrixBase::Unit{X,Y,Z}, AngleAxis can be used to easily * mimic Euler-angles. Here is an example: * \include AngleAxis_mimic_euler.cpp @@ -85,7 +83,7 @@ public: AngleAxis() {} /** Constructs and initialize the angle-axis rotation from an \a angle in radian * and an \a axis which \b must \b be \b normalized. - * + * * \warning If the \a axis vector is not normalized, then the angle-axis object * represents an invalid rotation. */ template diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in index a66750c3e..dba66c0d9 100644 --- a/doc/Doxyfile.in +++ b/doc/Doxyfile.in @@ -212,7 +212,6 @@ ALIASES = "only_for_vectors=This is only for vectors (either row- "svd_module=This is defined in the %SVD module. \code #include \endcode" \ "geometry_module=This is defined in the %Geometry module. \code #include \endcode" \ "leastsquares_module=This is defined in the %LeastSquares module. \code #include \endcode" \ - "addexample=\anchor" \ "label=\bug" \ "redstar=*" \ "nonstableyet=\warning This is not considered to be part of the stable public API yet. Changes may happen in future releases. See \ref Experimental \"Experimental parts of Eigen\"" diff --git a/unsupported/Eigen/BVH b/unsupported/Eigen/BVH index 7b9c3c7c6..f307da2f7 100644 --- a/unsupported/Eigen/BVH +++ b/unsupported/Eigen/BVH @@ -94,8 +94,6 @@ namespace Eigen { * responsibility of the intersectObject function to keep track of the results in whatever manner is appropriate. * The cartesian product intersection and the BVMinimize queries are similar--see their individual documentation. * - * \addexample BVH_Example \label How to use a BVH to find the closest pair between two point sets - * * The following is a simple but complete example for how to use the BVH to accelerate the search for a closest red-blue point pair: * \include BVH_Example.cpp * Output: \verbinclude BVH_Example.out diff --git a/unsupported/doc/Doxyfile.in b/unsupported/doc/Doxyfile.in index c33986224..561f18fe7 100644 --- a/unsupported/doc/Doxyfile.in +++ b/unsupported/doc/Doxyfile.in @@ -211,7 +211,6 @@ ALIASES = "only_for_vectors=This is only for vectors (either row- "svd_module=This is defined in the %SVD module. \code #include \endcode" \ "geometry_module=This is defined in the %Geometry module. \code #include \endcode" \ "leastsquares_module=This is defined in the %LeastSquares module. \code #include \endcode" \ - "addexample=\anchor" \ "label=\bug" \ "redstar=*" \ "nonstableyet=\warning This is not considered to be part of the stable public API yet. Changes may happen in future releases. See \ref Experimental \"Experimental parts of Eigen\"" -- cgit v1.2.3 From a54b99fa72e34a4ed6da643f32517a43a4ae76b6 Mon Sep 17 00:00:00 2001 From: Gael Guennebaud Date: Thu, 3 Sep 2009 11:39:44 +0200 Subject: move eigen values related stuff of the QR module to a new EigenSolver module. - perhaps we can find a better name ? - note that the QR module still includes the EigenSolver module for compatibility --- Eigen/CMakeLists.txt | 2 +- Eigen/EigenSolver | 74 +++ Eigen/LeastSquares | 2 +- Eigen/QR | 19 +- Eigen/src/CMakeLists.txt | 1 + Eigen/src/EigenSolver/CMakeLists.txt | 6 + Eigen/src/EigenSolver/ComplexEigenSolver.h | 139 +++++ Eigen/src/EigenSolver/ComplexSchur.h | 238 ++++++++ Eigen/src/EigenSolver/EigenSolver.h | 723 ++++++++++++++++++++++++ Eigen/src/EigenSolver/HessenbergDecomposition.h | 206 +++++++ Eigen/src/EigenSolver/SelfAdjointEigenSolver.h | 366 ++++++++++++ Eigen/src/EigenSolver/Tridiagonalization.h | 317 +++++++++++ Eigen/src/QR/ComplexEigenSolver.h | 138 ----- Eigen/src/QR/ComplexSchur.h | 237 -------- Eigen/src/QR/EigenSolver.h | 723 ------------------------ Eigen/src/QR/HessenbergDecomposition.h | 206 ------- Eigen/src/QR/QrInstantiations.cpp | 5 - Eigen/src/QR/SelfAdjointEigenSolver.h | 366 ------------ Eigen/src/QR/Tridiagonalization.h | 317 ----------- doc/Doxyfile.in | 1 + test/eigensolver_complex.cpp | 2 +- test/eigensolver_generic.cpp | 2 +- test/eigensolver_selfadjoint.cpp | 2 +- test/product_notemporary.cpp | 2 - 24 files changed, 2080 insertions(+), 2014 deletions(-) create mode 100644 Eigen/EigenSolver create mode 100644 Eigen/src/EigenSolver/CMakeLists.txt create mode 100644 Eigen/src/EigenSolver/ComplexEigenSolver.h create mode 100644 Eigen/src/EigenSolver/ComplexSchur.h create mode 100644 Eigen/src/EigenSolver/EigenSolver.h create mode 100644 Eigen/src/EigenSolver/HessenbergDecomposition.h create mode 100644 Eigen/src/EigenSolver/SelfAdjointEigenSolver.h create mode 100644 Eigen/src/EigenSolver/Tridiagonalization.h delete mode 100644 Eigen/src/QR/ComplexEigenSolver.h delete mode 100644 Eigen/src/QR/ComplexSchur.h delete mode 100644 Eigen/src/QR/EigenSolver.h delete mode 100644 Eigen/src/QR/HessenbergDecomposition.h delete mode 100644 Eigen/src/QR/SelfAdjointEigenSolver.h delete mode 100644 Eigen/src/QR/Tridiagonalization.h diff --git a/Eigen/CMakeLists.txt b/Eigen/CMakeLists.txt index b692cdc51..ebdf57812 100644 --- a/Eigen/CMakeLists.txt +++ b/Eigen/CMakeLists.txt @@ -1,4 +1,4 @@ -set(Eigen_HEADERS Core LU Cholesky QR Geometry Sparse Array SVD LeastSquares QtAlignedMalloc StdVector Householder Jacobi) +set(Eigen_HEADERS Core LU Cholesky QR Geometry Sparse Array SVD LeastSquares QtAlignedMalloc StdVector Householder Jacobi EigenSolver) if(EIGEN_BUILD_LIB) set(Eigen_SRCS diff --git a/Eigen/EigenSolver b/Eigen/EigenSolver new file mode 100644 index 000000000..fd126d282 --- /dev/null +++ b/Eigen/EigenSolver @@ -0,0 +1,74 @@ +#ifndef EIGEN_EIGEN_SOLVER_MODULE_H +#define EIGEN_EIGEN_SOLVER_MODULE_H + +#include "Core" + +#include "src/Core/util/DisableMSVCWarnings.h" + +#include "Cholesky" +#include "Jacobi" +#include "Householder" + +// Note that EIGEN_HIDE_HEAVY_CODE has to be defined per module +#if (defined EIGEN_EXTERN_INSTANTIATIONS) && (EIGEN_EXTERN_INSTANTIATIONS>=2) + #ifndef EIGEN_HIDE_HEAVY_CODE + #define EIGEN_HIDE_HEAVY_CODE + #endif +#elif defined EIGEN_HIDE_HEAVY_CODE + #undef EIGEN_HIDE_HEAVY_CODE +#endif + +namespace Eigen { + +/** \defgroup EigenSolver_Module Eigen Solver module + * + * \nonstableyet + * + * This module mainly provides various eigen value solvers. + * This module also provides some MatrixBase methods, including: + * - MatrixBase::eigenvalues(), + * - MatrixBase::operatorNorm() + * + * \code + * #include + * \endcode + */ + +#include "src/EigenSolver/Tridiagonalization.h" +#include "src/EigenSolver/EigenSolver.h" +#include "src/EigenSolver/SelfAdjointEigenSolver.h" +#include "src/EigenSolver/HessenbergDecomposition.h" +#include "src/EigenSolver/ComplexSchur.h" +#include "src/EigenSolver/ComplexEigenSolver.h" + +// declare all classes for a given matrix type +#define EIGEN_EIGEN_SOLVER_MODULE_INSTANTIATE_TYPE(MATRIXTYPE,PREFIX) \ + PREFIX template class Tridiagonalization; \ + PREFIX template class HessenbergDecomposition; \ + PREFIX template class SelfAdjointEigenSolver + +// removed because it does not support complex yet +// PREFIX template class EigenSolver + +// declare all class for all types +#define EIGEN_EIGEN_SOLVER_MODULE_INSTANTIATE(PREFIX) \ + EIGEN_EIGEN_SOLVER_MODULE_INSTANTIATE_TYPE(Matrix2f,PREFIX); \ + EIGEN_EIGEN_SOLVER_MODULE_INSTANTIATE_TYPE(Matrix2d,PREFIX); \ + EIGEN_EIGEN_SOLVER_MODULE_INSTANTIATE_TYPE(Matrix3f,PREFIX); \ + EIGEN_EIGEN_SOLVER_MODULE_INSTANTIATE_TYPE(Matrix3d,PREFIX); \ + EIGEN_EIGEN_SOLVER_MODULE_INSTANTIATE_TYPE(Matrix4f,PREFIX); \ + EIGEN_EIGEN_SOLVER_MODULE_INSTANTIATE_TYPE(Matrix4d,PREFIX); \ + EIGEN_EIGEN_SOLVER_MODULE_INSTANTIATE_TYPE(MatrixXf,PREFIX); \ + EIGEN_EIGEN_SOLVER_MODULE_INSTANTIATE_TYPE(MatrixXd,PREFIX); \ + EIGEN_EIGEN_SOLVER_MODULE_INSTANTIATE_TYPE(MatrixXcf,PREFIX); \ + EIGEN_EIGEN_SOLVER_MODULE_INSTANTIATE_TYPE(MatrixXcd,PREFIX) + +#ifdef EIGEN_EXTERN_INSTANTIATIONS + EIGEN_EIGEN_SOLVER_MODULE_INSTANTIATE(extern); +#endif // EIGEN_EXTERN_INSTANTIATIONS + +} // namespace Eigen + +#include "src/Core/util/EnableMSVCWarnings.h" + +#endif // EIGEN_EIGEN_SOLVER_MODULE_H diff --git a/Eigen/LeastSquares b/Eigen/LeastSquares index 573a13cb4..db620f548 100644 --- a/Eigen/LeastSquares +++ b/Eigen/LeastSquares @@ -5,7 +5,7 @@ #include "src/Core/util/DisableMSVCWarnings.h" -#include "QR" +#include "EigenSolver" #include "Geometry" namespace Eigen { diff --git a/Eigen/QR b/Eigen/QR index 4b49004c3..a7273bc8a 100644 --- a/Eigen/QR +++ b/Eigen/QR @@ -24,11 +24,9 @@ namespace Eigen { * * \nonstableyet * - * This module mainly provides QR decomposition and an eigen value solver. + * This module provides various QR decompositions * This module also provides some MatrixBase methods, including: * - MatrixBase::qr(), - * - MatrixBase::eigenvalues(), - * - MatrixBase::operatorNorm() * * \code * #include @@ -38,22 +36,10 @@ namespace Eigen { #include "src/QR/HouseholderQR.h" #include "src/QR/FullPivotingHouseholderQR.h" #include "src/QR/ColPivotingHouseholderQR.h" -#include "src/QR/Tridiagonalization.h" -#include "src/QR/EigenSolver.h" -#include "src/QR/SelfAdjointEigenSolver.h" -#include "src/QR/HessenbergDecomposition.h" -#include "src/QR/ComplexSchur.h" -#include "src/QR/ComplexEigenSolver.h" // declare all classes for a given matrix type #define EIGEN_QR_MODULE_INSTANTIATE_TYPE(MATRIXTYPE,PREFIX) \ PREFIX template class HouseholderQR; \ - PREFIX template class Tridiagonalization; \ - PREFIX template class HessenbergDecomposition; \ - PREFIX template class SelfAdjointEigenSolver - -// removed because it does not support complex yet -// PREFIX template class EigenSolver // declare all class for all types #define EIGEN_QR_MODULE_INSTANTIATE(PREFIX) \ @@ -76,4 +62,7 @@ namespace Eigen { #include "src/Core/util/EnableMSVCWarnings.h" +// FIXME for compatibility we include EigenSolver here: +#include "EigenSolver" + #endif // EIGEN_QR_MODULE_H diff --git a/Eigen/src/CMakeLists.txt b/Eigen/src/CMakeLists.txt index 2f6a83a1b..6b1333860 100644 --- a/Eigen/src/CMakeLists.txt +++ b/Eigen/src/CMakeLists.txt @@ -9,3 +9,4 @@ ADD_SUBDIRECTORY(LeastSquares) ADD_SUBDIRECTORY(Sparse) ADD_SUBDIRECTORY(Jacobi) ADD_SUBDIRECTORY(Householder) +ADD_SUBDIRECTORY(EigenSolver) diff --git a/Eigen/src/EigenSolver/CMakeLists.txt b/Eigen/src/EigenSolver/CMakeLists.txt new file mode 100644 index 000000000..63bc75e0c --- /dev/null +++ b/Eigen/src/EigenSolver/CMakeLists.txt @@ -0,0 +1,6 @@ +FILE(GLOB Eigen_EIGENSOLVER_SRCS "*.h") + +INSTALL(FILES + ${Eigen_EIGENSOLVER_SRCS} + DESTINATION ${INCLUDE_INSTALL_DIR}/Eigen/src/EigenSolver COMPONENT Devel + ) diff --git a/Eigen/src/EigenSolver/ComplexEigenSolver.h b/Eigen/src/EigenSolver/ComplexEigenSolver.h new file mode 100644 index 000000000..2ea7464a6 --- /dev/null +++ b/Eigen/src/EigenSolver/ComplexEigenSolver.h @@ -0,0 +1,139 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2009 Claire Maurice +// Copyright (C) 2009 Gael Guennebaud +// +// 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 . + +#ifndef EIGEN_COMPLEX_EIGEN_SOLVER_H +#define EIGEN_COMPLEX_EIGEN_SOLVER_H + +#define MAXITER 30 + +template class ComplexEigenSolver +{ + public: + typedef _MatrixType MatrixType; + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits::Real RealScalar; + typedef std::complex Complex; + typedef Matrix EigenvalueType; + typedef Matrix EigenvectorType; + + /** + * \brief Default Constructor. + * + * The default constructor is useful in cases in which the user intends to + * perform decompositions via ComplexEigenSolver::compute(const MatrixType&). + */ + ComplexEigenSolver() : m_eivec(), m_eivalues(), m_isInitialized(false) + {} + + ComplexEigenSolver(const MatrixType& matrix) + : m_eivec(matrix.rows(),matrix.cols()), + m_eivalues(matrix.cols()), + m_isInitialized(false) + { + compute(matrix); + } + + EigenvectorType eigenvectors(void) const + { + ei_assert(m_isInitialized && "ComplexEigenSolver is not initialized."); + return m_eivec; + } + + EigenvalueType eigenvalues() const + { + ei_assert(m_isInitialized && "ComplexEigenSolver is not initialized."); + return m_eivalues; + } + + void compute(const MatrixType& matrix); + + protected: + MatrixType m_eivec; + EigenvalueType m_eivalues; + bool m_isInitialized; +}; + + +template +void ComplexEigenSolver::compute(const MatrixType& matrix) +{ + // this code is inspired from Jampack + assert(matrix.cols() == matrix.rows()); + int n = matrix.cols(); + m_eivalues.resize(n,1); + + RealScalar eps = epsilon(); + + // Reduce to complex Schur form + ComplexSchur schur(matrix); + + m_eivalues = schur.matrixT().diagonal(); + + m_eivec.setZero(); + + Scalar d2, z; + RealScalar norm = matrix.norm(); + + // compute the (normalized) eigenvectors + for(int k=n-1 ; k>=0 ; k--) + { + d2 = schur.matrixT().coeff(k,k); + m_eivec.coeffRef(k,k) = Scalar(1.0,0.0); + for(int i=k-1 ; i>=0 ; i--) + { + m_eivec.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; + + } + m_eivec.col(k).normalize(); + } + + m_eivec = schur.matrixU() * m_eivec; + m_isInitialized = true; + + // sort the eigenvalues + { + for (int i=0; i +// +// 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 . + +#ifndef EIGEN_COMPLEX_SCHUR_H +#define EIGEN_COMPLEX_SCHUR_H + +#define MAXITER 30 + +/** \ingroup QR + * + * \class ComplexShur + * + * \brief Performs a complex Shur decomposition of a real or complex square matrix + * + */ +template class ComplexSchur +{ + public: + typedef _MatrixType MatrixType; + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits::Real RealScalar; + typedef std::complex Complex; + typedef Matrix ComplexMatrixType; + + /** + * \brief Default Constructor. + * + * The default constructor is useful in cases in which the user intends to + * perform decompositions via ComplexSchur::compute(const MatrixType&). + */ + ComplexSchur() : m_matT(), m_matU(), m_isInitialized(false) + {} + + ComplexSchur(const MatrixType& matrix) + : m_matT(matrix.rows(),matrix.cols()), + m_matU(matrix.rows(),matrix.cols()), + m_isInitialized(false) + { + compute(matrix); + } + + ComplexMatrixType matrixU() const + { + ei_assert(m_isInitialized && "ComplexSchur is not initialized."); + return m_matU; + } + + ComplexMatrixType matrixT() const + { + ei_assert(m_isInitialized && "ComplexShur is not initialized."); + return m_matT; + } + + void compute(const MatrixType& matrix); + + protected: + ComplexMatrixType m_matT, m_matU; + bool m_isInitialized; +}; + +/** Computes the principal value of the square root of the complex \a z. */ +template +std::complex ei_sqrt(const std::complex &z) +{ + RealScalar t, tre, tim; + + t = ei_abs(z); + + if (ei_abs(ei_real(z)) <= ei_abs(ei_real(z))) + { + // No cancellation in these formulas + tre = ei_sqrt(0.5*(t + ei_real(z))); + tim = ei_sqrt(0.5*(t - ei_real(z))); + } + else + { + // Stable computation of the above formulas + if (z.real() > 0) + { + tre = t + z.real(); + tim = ei_abs(ei_imag(z))*ei_sqrt(0.5/tre); + tre = ei_sqrt(0.5*tre); + } + else + { + tim = t - z.real(); + tre = ei_abs(ei_imag(z))*ei_sqrt(0.5/tim); + tim = ei_sqrt(0.5*tim); + } + } + if(z.imag() < 0) + tim = -tim; + + return (std::complex(tre,tim)); + +} + +template +void ComplexSchur::compute(const MatrixType& matrix) +{ + // this code is inspired from Jampack + assert(matrix.cols() == matrix.rows()); + int n = matrix.cols(); + + // Reduce to Hessenberg form + HessenbergDecomposition hess(matrix); + + m_matT = hess.matrixH(); + m_matU = hess.matrixQ(); + + int iu = m_matT.cols() - 1; + int il; + RealScalar d,sd,sf; + Complex c,b,disc,r1,r2,kappa; + + RealScalar eps = epsilon(); + + int iter = 0; + while(true) + { + //locate the range in which to iterate + while(iu > 0) + { + d = ei_norm1(m_matT.coeffRef(iu,iu)) + ei_norm1(m_matT.coeffRef(iu-1,iu-1)); + sd = ei_norm1(m_matT.coeffRef(iu,iu-1)); + + if(sd >= eps * d) break; // FIXME : precision criterion ?? + + m_matT.coeffRef(iu,iu-1) = Complex(0); + iter = 0; + --iu; + } + if(iu==0) break; + iter++; + + if(iter >= MAXITER) + { + // FIXME : what to do when iter==MAXITER ?? + std::cerr << "MAXITER" << std::endl; + return; + } + + il = iu-1; + while( il > 0 ) + { + // check if the current 2x2 block on the diagonal is upper triangular + d = ei_norm1(m_matT.coeffRef(il,il)) + ei_norm1(m_matT.coeffRef(il-1,il-1)); + sd = ei_norm1(m_matT.coeffRef(il,il-1)); + + if(sd < eps * d) break; // FIXME : precision criterion ?? + + --il; + } + + if( il != 0 ) m_matT.coeffRef(il,il-1) = Complex(0); + + // compute the shift (the normalization by sf is to avoid under/overflow) + Matrix t = m_matT.template block<2,2>(iu-1,iu-1); + sf = t.cwise().abs().sum(); + t /= sf; + + c = t.determinant(); + b = t.diagonal().sum(); + + disc = ei_sqrt(b*b - RealScalar(4)*c); + + 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; + + // perform the QR step using Givens rotations + PlanarRotation rot; + rot.makeGivens(m_matT.coeff(il,il) - kappa, m_matT.coeff(il+1,il)); + + for(int i=il ; i +// +// 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 . + +#ifndef EIGEN_EIGENSOLVER_H +#define EIGEN_EIGENSOLVER_H + +/** \ingroup EigenSolver_Module + * \nonstableyet + * + * \class EigenSolver + * + * \brief Eigen values/vectors solver for non selfadjoint matrices + * + * \param MatrixType the type of the matrix of which we are computing the eigen decomposition + * + * Currently it only support real matrices. + * + * \note this code was adapted from JAMA (public domain) + * + * \sa MatrixBase::eigenvalues(), SelfAdjointEigenSolver + */ +template class EigenSolver +{ + public: + + typedef _MatrixType MatrixType; + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits::Real RealScalar; + typedef std::complex Complex; + typedef Matrix EigenvalueType; + typedef Matrix EigenvectorType; + typedef Matrix RealVectorType; + typedef Matrix RealVectorTypeX; + + /** + * \brief Default Constructor. + * + * The default constructor is useful in cases in which the user intends to + * perform decompositions via EigenSolver::compute(const MatrixType&). + */ + EigenSolver() : m_eivec(), m_eivalues(), m_isInitialized(false) {} + + EigenSolver(const MatrixType& matrix) + : m_eivec(matrix.rows(), matrix.cols()), + m_eivalues(matrix.cols()), + m_isInitialized(false) + { + compute(matrix); + } + + + EigenvectorType eigenvectors(void) const; + + /** \returns a real matrix V of pseudo eigenvectors. + * + * 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. + * + * 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$ + * + * \sa pseudoEigenvalueMatrix() + */ + const MatrixType& pseudoEigenvectors() const + { + ei_assert(m_isInitialized && "EigenSolver is not initialized."); + return m_eivec; + } + + MatrixType pseudoEigenvalueMatrix() const; + + /** \returns the eigenvalues as a column vector */ + EigenvalueType eigenvalues() const + { + ei_assert(m_isInitialized && "EigenSolver is not initialized."); + return m_eivalues; + } + + EigenSolver& compute(const MatrixType& matrix); + + private: + + void orthes(MatrixType& matH, RealVectorType& ort); + void hqr2(MatrixType& matH); + + protected: + MatrixType m_eivec; + EigenvalueType m_eivalues; + bool m_isInitialized; +}; + +/** \returns the real block diagonal matrix D of the eigenvalues. + * + * See pseudoEigenvectors() for the details. + */ +template +MatrixType EigenSolver::pseudoEigenvalueMatrix() const +{ + ei_assert(m_isInitialized && "EigenSolver is not initialized."); + int n = m_eivec.cols(); + MatrixType matD = MatrixType::Zero(n,n); + for (int i=0; i(i,i) << ei_real(m_eivalues.coeff(i)), ei_imag(m_eivalues.coeff(i)), + -ei_imag(m_eivalues.coeff(i)), ei_real(m_eivalues.coeff(i)); + ++i; + } + } + return matD; +} + +/** \returns the normalized complex eigenvectors as a matrix of column vectors. + * + * \sa eigenvalues(), pseudoEigenvectors() + */ +template +typename EigenSolver::EigenvectorType EigenSolver::eigenvectors(void) const +{ + ei_assert(m_isInitialized && "EigenSolver is not initialized."); + int n = m_eivec.cols(); + EigenvectorType matV(n,n); + for (int j=0; j(); + } + else + { + // we have a pair of complex eigen values + for (int i=0; i +EigenSolver& EigenSolver::compute(const MatrixType& matrix) +{ + assert(matrix.cols() == matrix.rows()); + int n = matrix.cols(); + m_eivalues.resize(n,1); + + MatrixType matH = matrix; + RealVectorType ort(n); + + // Reduce to Hessenberg form. + orthes(matH, ort); + + // Reduce Hessenberg to real Schur form. + hqr2(matH); + + m_isInitialized = true; + return *this; +} + +// Nonsymmetric reduction to Hessenberg form. +template +void EigenSolver::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).cwise().abs().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 +std::complex cdiv(Scalar xr, Scalar xi, Scalar yr, Scalar yi) +{ + Scalar r,d; + if (ei_abs(yr) > ei_abs(yi)) + { + r = yi/yr; + d = yr + r*yi; + return std::complex((xr + r*xi)/d, (xi - r*xr)/d); + } + else + { + r = yr/yi; + d = yi + r*yr; + return std::complex((r*xr + xi)/d, (r*xi - xr)/d); + } +} + + +// Nonsymmetric reduction from Hessenberg to real Schur form. +template +void EigenSolver::hqr2(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. + + // 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::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().cwise().abs().sum() + matH.corner(BottomLeft,n,n).diagonal().cwise().abs().sum(); + 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)).cwise().abs().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--) + { + p = m_eivalues.coeff(n).real(); + q = m_eivalues.coeff(n).imag(); + + // Scalar vector + if (q == 0) + { + int l = n; + matH.coeffRef(n,n) = 1.0; + for (int i = n-1; i >= 0; i--) + { + w = matH.coeff(i,i) - p; + r = matH.row(i).segment(l,n-l+1).dot(matH.col(n).segment(l, n-l+1)); + + if (m_eivalues.coeff(i).imag() < 0.0) + { + z = w; + s = r; + } + else + { + l = i; + if (m_eivalues.coeff(i).imag() == 0.0) + { + if (w != 0.0) + matH.coeffRef(i,n) = -r / w; + else + matH.coeffRef(i,n) = -r / (eps * norm); + } + else // Solve real equations + { + x = matH.coeff(i,i+1); + y = matH.coeff(i+1,i); + q = (m_eivalues.coeff(i).real() - p) * (m_eivalues.coeff(i).real() - p) + m_eivalues.coeff(i).imag() * m_eivalues.coeff(i).imag(); + t = (x * s - z * r) / q; + matH.coeffRef(i,n) = t; + if (ei_abs(x) > ei_abs(z)) + matH.coeffRef(i+1,n) = (-r - w * t) / x; + else + matH.coeffRef(i+1,n) = (-s - y * t) / z; + } + + // Overflow control + t = ei_abs(matH.coeff(i,n)); + if ((eps * t) * t > 1) + matH.col(n).end(nn-i) /= t; + } + } + } + else if (q < 0) // Complex vector + { + std::complex cc; + int l = n-1; + + // Last vector component imaginary so matrix is triangular + if (ei_abs(matH.coeff(n,n-1)) > ei_abs(matH.coeff(n-1,n))) + { + matH.coeffRef(n-1,n-1) = q / matH.coeff(n,n-1); + matH.coeffRef(n-1,n) = -(matH.coeff(n,n) - p) / matH.coeff(n,n-1); + } + else + { + cc = cdiv(0.0,-matH.coeff(n-1,n),matH.coeff(n-1,n-1)-p,q); + matH.coeffRef(n-1,n-1) = ei_real(cc); + matH.coeffRef(n-1,n) = ei_imag(cc); + } + matH.coeffRef(n,n-1) = 0.0; + matH.coeffRef(n,n) = 1.0; + for (int i = n-2; i >= 0; i--) + { + Scalar ra,sa,vr,vi; + ra = matH.row(i).segment(l, n-l+1).dot(matH.col(n-1).segment(l, n-l+1)); + sa = matH.row(i).segment(l, n-l+1).dot(matH.col(n).segment(l, n-l+1)); + w = matH.coeff(i,i) - p; + + if (m_eivalues.coeff(i).imag() < 0.0) + { + z = w; + r = ra; + s = sa; + } + else + { + l = i; + if (m_eivalues.coeff(i).imag() == 0) + { + cc = cdiv(-ra,-sa,w,q); + matH.coeffRef(i,n-1) = ei_real(cc); + matH.coeffRef(i,n) = ei_imag(cc); + } + else + { + // Solve complex equations + x = matH.coeff(i,i+1); + y = matH.coeff(i+1,i); + vr = (m_eivalues.coeff(i).real() - p) * (m_eivalues.coeff(i).real() - p) + m_eivalues.coeff(i).imag() * m_eivalues.coeff(i).imag() - q * q; + vi = (m_eivalues.coeff(i).real() - p) * Scalar(2) * q; + if ((vr == 0.0) && (vi == 0.0)) + vr = eps * norm * (ei_abs(w) + ei_abs(q) + ei_abs(x) + ei_abs(y) + ei_abs(z)); + + cc= cdiv(x*r-z*ra+q*sa,x*s-z*sa-q*ra,vr,vi); + matH.coeffRef(i,n-1) = ei_real(cc); + matH.coeffRef(i,n) = ei_imag(cc); + if (ei_abs(x) > (ei_abs(z) + ei_abs(q))) + { + matH.coeffRef(i+1,n-1) = (-ra - w * matH.coeff(i,n-1) + q * matH.coeff(i,n)) / x; + matH.coeffRef(i+1,n) = (-sa - w * matH.coeff(i,n) - q * matH.coeff(i,n-1)) / x; + } + else + { + cc = cdiv(-r-y*matH.coeff(i,n-1),-s-y*matH.coeff(i,n),z,q); + matH.coeffRef(i+1,n-1) = ei_real(cc); + matH.coeffRef(i+1,n) = ei_imag(cc); + } + } + + // Overflow control + t = std::max(ei_abs(matH.coeff(i,n-1)),ei_abs(matH.coeff(i,n))); + if ((eps * t) * t > 1) + matH.block(i, n-1, nn-i, 2) /= t; + + } + } + } + } + + // Vectors of isolated roots + for (int i = 0; i < nn; ++i) + { + // FIXME again what's the purpose of this test ? + // in this algo low==0 and high==nn-1 !! + if (i < low || i > high) + { + m_eivec.row(i).end(nn-i) = matH.row(i).end(nn-i); + } + } + + // Back transformation to get eigenvectors of original matrix + int bRows = high-low+1; + for (int j = nn-1; j >= low; j--) + { + int bSize = std::min(j,high)-low+1; + m_eivec.col(j).segment(low, bRows) = (m_eivec.block(low, low, bRows, bSize) * matH.col(j).segment(low, bSize)); + } +} + +#endif // EIGEN_EIGENSOLVER_H diff --git a/Eigen/src/EigenSolver/HessenbergDecomposition.h b/Eigen/src/EigenSolver/HessenbergDecomposition.h new file mode 100644 index 000000000..f782cef30 --- /dev/null +++ b/Eigen/src/EigenSolver/HessenbergDecomposition.h @@ -0,0 +1,206 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008 Gael Guennebaud +// +// 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 . + +#ifndef EIGEN_HESSENBERGDECOMPOSITION_H +#define EIGEN_HESSENBERGDECOMPOSITION_H + +/** \ingroup EigenSolver_Module + * \nonstableyet + * + * \class HessenbergDecomposition + * + * \brief Reduces a squared matrix to an Hessemberg form + * + * \param 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. + * + * \sa class Tridiagonalization, class Qr + */ +template class HessenbergDecomposition +{ + public: + + typedef _MatrixType MatrixType; + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits::Real RealScalar; + + enum { + Size = MatrixType::RowsAtCompileTime, + SizeMinusOne = MatrixType::RowsAtCompileTime==Dynamic + ? Dynamic + : MatrixType::RowsAtCompileTime-1 + }; + + typedef Matrix CoeffVectorType; + typedef Matrix DiagonalType; + typedef Matrix SubDiagonalType; + + typedef typename NestByValue >::RealReturnType DiagonalReturnType; + + typedef typename NestByValue >,0 > >::RealReturnType SubDiagonalReturnType; + + /** This constructor initializes a HessenbergDecomposition object for + * further use with HessenbergDecomposition::compute() + */ + HessenbergDecomposition(int size = Size==Dynamic ? 2 : Size) + : m_matrix(size,size), m_hCoeffs(size-1) + {} + + HessenbergDecomposition(const MatrixType& matrix) + : m_matrix(matrix), + m_hCoeffs(matrix.cols()-1) + { + _compute(m_matrix, m_hCoeffs); + } + + /** Computes or re-compute the Hessenberg decomposition for the matrix \a matrix. + * + * This method allows to re-use the allocated data. + */ + void compute(const MatrixType& matrix) + { + m_matrix = matrix; + m_hCoeffs.resize(matrix.rows()-1,1); + _compute(m_matrix, m_hCoeffs); + } + + /** \returns the householder coefficients allowing to + * reconstruct the matrix Q from the packed data. + * + * \sa packedMatrix() + */ + CoeffVectorType householderCoefficients() const { return m_hCoeffs; } + + /** \returns the internal result of the decomposition. + * + * 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) ] + * + * See LAPACK for further details on this packed storage. + */ + const MatrixType& packedMatrix(void) const { return m_matrix; } + + MatrixType matrixQ() const; + MatrixType matrixH() const; + + private: + + static void _compute(MatrixType& matA, CoeffVectorType& hCoeffs); + + protected: + MatrixType m_matrix; + CoeffVectorType m_hCoeffs; +}; + +#ifndef EIGEN_HIDE_HEAVY_CODE + +/** \internal + * Performs a tridiagonal decomposition of \a matA in place. + * + * \param matA the input selfadjoint matrix + * \param hCoeffs returned Householder coefficients + * + * The result is written in the lower triangular part of \a matA. + * + * Implemented from Golub's "Matrix Computations", algorithm 8.3.1. + * + * \sa packedMatrix() + */ +template +void HessenbergDecomposition::_compute(MatrixType& matA, CoeffVectorType& hCoeffs) +{ + assert(matA.rows()==matA.cols()); + int n = matA.rows(); + Matrix temp(n); + for (int i = 0; i +typename HessenbergDecomposition::MatrixType +HessenbergDecomposition::matrixQ() const +{ + int n = m_matrix.rows(); + MatrixType matQ = MatrixType::Identity(n,n); + Matrix temp(n); + for (int i = n-2; i>=0; i--) + { + matQ.corner(BottomRight,n-i-1,n-i-1) + .applyHouseholderOnTheLeft(m_matrix.col(i).end(n-i-2), ei_conj(m_hCoeffs.coeff(i)), &temp.coeffRef(0,0)); + } + return matQ; +} + +#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 HessenbergDecomposition::MatrixType +HessenbergDecomposition::matrixH() const +{ + // FIXME should this function (and other similar) rather take a matrix as argument + // and fill it (to avoid temporaries) + int n = m_matrix.rows(); + MatrixType matH = m_matrix; + if (n>2) + matH.corner(BottomLeft,n-2, n-2).template triangularView().setZero(); + return matH; +} + +#endif // EIGEN_HESSENBERGDECOMPOSITION_H diff --git a/Eigen/src/EigenSolver/SelfAdjointEigenSolver.h b/Eigen/src/EigenSolver/SelfAdjointEigenSolver.h new file mode 100644 index 000000000..40b06df2c --- /dev/null +++ b/Eigen/src/EigenSolver/SelfAdjointEigenSolver.h @@ -0,0 +1,366 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008 Gael Guennebaud +// +// 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 . + +#ifndef EIGEN_SELFADJOINTEIGENSOLVER_H +#define EIGEN_SELFADJOINTEIGENSOLVER_H + +/** \eigensolver_module \ingroup EigenSolver_Module + * \nonstableyet + * + * \class SelfAdjointEigenSolver + * + * \brief Eigen values/vectors solver for selfadjoint matrix + * + * \param MatrixType the type of the matrix of which we are computing the eigen decomposition + * + * \note MatrixType must be an actual Matrix type, it can't be an expression type. + * + * \sa MatrixBase::eigenvalues(), class EigenSolver + */ +template class SelfAdjointEigenSolver +{ + public: + + enum {Size = _MatrixType::RowsAtCompileTime }; + typedef _MatrixType MatrixType; + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits::Real RealScalar; + typedef std::complex Complex; + typedef Matrix RealVectorType; + typedef Matrix RealVectorTypeX; + typedef Tridiagonalization TridiagonalizationType; +// typedef typename TridiagonalizationType::TridiagonalMatrixType TridiagonalMatrixType; + + SelfAdjointEigenSolver() + : m_eivec(int(Size), int(Size)), + m_eivalues(int(Size)) + { + ei_assert(Size!=Dynamic); + } + + SelfAdjointEigenSolver(int size) + : m_eivec(size, size), + m_eivalues(size) + {} + + /** Constructors computing the eigenvalues of the selfadjoint matrix \a matrix, + * as well as the eigenvectors if \a computeEigenvectors is true. + * + * \sa compute(MatrixType,bool), SelfAdjointEigenSolver(MatrixType,MatrixType,bool) + */ + SelfAdjointEigenSolver(const MatrixType& matrix, bool computeEigenvectors = true) + : m_eivec(matrix.rows(), matrix.cols()), + m_eivalues(matrix.cols()) + { + compute(matrix, computeEigenvectors); + } + + /** Constructors computing the eigenvalues of the generalized eigen problem + * \f$ Ax = lambda B x \f$ with \a matA the selfadjoint matrix \f$ A \f$ + * and \a matB the positive definite matrix \f$ B \f$ . The eigenvectors + * are computed if \a computeEigenvectors is true. + * + * \sa compute(MatrixType,MatrixType,bool), SelfAdjointEigenSolver(MatrixType,bool) + */ + SelfAdjointEigenSolver(const MatrixType& matA, const MatrixType& matB, bool computeEigenvectors = true) + : m_eivec(matA.rows(), matA.cols()), + m_eivalues(matA.cols()) + { + compute(matA, matB, computeEigenvectors); + } + + SelfAdjointEigenSolver& compute(const MatrixType& matrix, bool computeEigenvectors = true); + + SelfAdjointEigenSolver& compute(const MatrixType& matA, const MatrixType& matB, bool computeEigenvectors = true); + + /** \returns the computed eigen vectors as a matrix of column vectors */ + MatrixType eigenvectors(void) const + { + #ifndef NDEBUG + ei_assert(m_eigenvectorsOk); + #endif + return m_eivec; + } + + /** \returns the computed eigen values */ + RealVectorType eigenvalues(void) const { return m_eivalues; } + + /** \returns the positive square root of the matrix + * + * \note the matrix itself must be positive in order for this to make sense. + */ + MatrixType operatorSqrt() const + { + return m_eivec * m_eivalues.cwise().sqrt().asDiagonal() * m_eivec.adjoint(); + } + + /** \returns the positive inverse square root of the matrix + * + * \note the matrix itself must be positive definite in order for this to make sense. + */ + MatrixType operatorInverseSqrt() const + { + return m_eivec * m_eivalues.cwise().inverse().cwise().sqrt().asDiagonal() * m_eivec.adjoint(); + } + + + protected: + MatrixType m_eivec; + RealVectorType m_eivalues; + #ifndef NDEBUG + bool m_eigenvectorsOk; + #endif +}; + +#ifndef EIGEN_HIDE_HEAVY_CODE + +/** \internal + * + * \eigensolver_module + * + * Performs a QR step on a tridiagonal symmetric matrix represented as a + * pair of two vectors \a diag and \a subdiag. + * + * \param matA the input selfadjoint matrix + * \param hCoeffs returned Householder coefficients + * + * For compilation efficiency reasons, this procedure does not use eigen expression + * for its arguments. + * + * Implemented from Golub's "Matrix Computations", algorithm 8.3.2: + * "implicit symmetric QR step with Wilkinson shift" + */ +template +static void ei_tridiagonal_qr_step(RealScalar* diag, RealScalar* subdiag, int start, int end, Scalar* matrixQ, int n); + +/** Computes the eigenvalues of the selfadjoint matrix \a matrix, + * as well as the eigenvectors if \a computeEigenvectors is true. + * + * \sa SelfAdjointEigenSolver(MatrixType,bool), compute(MatrixType,MatrixType,bool) + */ +template +SelfAdjointEigenSolver& SelfAdjointEigenSolver::compute(const MatrixType& matrix, bool computeEigenvectors) +{ + #ifndef NDEBUG + m_eigenvectorsOk = computeEigenvectors; + #endif + assert(matrix.cols() == matrix.rows()); + int n = matrix.cols(); + m_eivalues.resize(n,1); + + if(n==1) + { + m_eivalues.coeffRef(0,0) = ei_real(matrix.coeff(0,0)); + m_eivec.setOnes(); + return *this; + } + + m_eivec = matrix; + + // FIXME, should tridiag be a local variable of this function or an attribute of SelfAdjointEigenSolver ? + // the latter avoids multiple memory allocation when the same SelfAdjointEigenSolver is used multiple times... + // (same for diag and subdiag) + RealVectorType& diag = m_eivalues; + typename TridiagonalizationType::SubDiagonalType subdiag(n-1); + TridiagonalizationType::decomposeInPlace(m_eivec, diag, subdiag, computeEigenvectors); + + int end = n-1; + int start = 0; + while (end>0) + { + for (int i = start; i0 && subdiag[end-1]==0) + end--; + if (end<=0) + break; + start = end - 1; + while (start>0 && subdiag[start-1]!=0) + start--; + + ei_tridiagonal_qr_step(diag.data(), subdiag.data(), start, end, computeEigenvectors ? m_eivec.data() : (Scalar*)0, n); + } + + // Sort eigenvalues and corresponding vectors. + // TODO make the sort optional ? + // TODO use a better sort algorithm !! + for (int i = 0; i < n-1; ++i) + { + int k; + m_eivalues.segment(i,n-i).minCoeff(&k); + if (k > 0) + { + std::swap(m_eivalues[i], m_eivalues[k+i]); + m_eivec.col(i).swap(m_eivec.col(k+i)); + } + } + return *this; +} + +/** Computes the eigenvalues of the generalized eigen problem + * \f$ Ax = lambda B x \f$ with \a matA the selfadjoint matrix \f$ A \f$ + * and \a matB the positive definite matrix \f$ B \f$ . The eigenvectors + * are computed if \a computeEigenvectors is true. + * + * \sa SelfAdjointEigenSolver(MatrixType,MatrixType,bool), compute(MatrixType,bool) + */ +template +SelfAdjointEigenSolver& SelfAdjointEigenSolver:: +compute(const MatrixType& matA, const MatrixType& matB, bool computeEigenvectors) +{ + ei_assert(matA.cols()==matA.rows() && matB.rows()==matA.rows() && matB.cols()==matB.rows()); + + // Compute the cholesky decomposition of matB = L L' + LLT cholB(matB); + + // compute C = inv(L) A inv(L') + MatrixType matC = matA; + cholB.matrixL().solveInPlace(matC); + // FIXME since we currently do not support A * inv(L'), let's do (inv(L) A')' : + matC.adjointInPlace(); + cholB.matrixL().solveInPlace(matC); + matC.adjointInPlace(); + // this version works too: +// matC = matC.transpose(); +// cholB.matrixL().conjugate().template marked().solveTriangularInPlace(matC); +// matC = matC.transpose(); + // FIXME: this should work: (currently it only does for small matrices) +// Transpose trMatC(matC); +// cholB.matrixL().conjugate().eval().template marked().solveTriangularInPlace(trMatC); + + compute(matC, computeEigenvectors); + + if (computeEigenvectors) + { + // transform back the eigen vectors: evecs = inv(U) * evecs + cholB.matrixU().solveInPlace(m_eivec); + for (int i=0; i +inline Matrix::Scalar>::Real, ei_traits::ColsAtCompileTime, 1> +MatrixBase::eigenvalues() const +{ + ei_assert(Flags&SelfAdjointBit); + return SelfAdjointEigenSolver(eval(),false).eigenvalues(); +} + +template +struct ei_operatorNorm_selector +{ + static inline typename NumTraits::Scalar>::Real + operatorNorm(const MatrixBase& m) + { + // FIXME if it is really guaranteed that the eigenvalues are already sorted, + // then we don't need to compute a maxCoeff() here, comparing the 1st and last ones is enough. + return m.eigenvalues().cwise().abs().maxCoeff(); + } +}; + +template struct ei_operatorNorm_selector +{ + static inline typename NumTraits::Scalar>::Real + operatorNorm(const MatrixBase& m) + { + typename Derived::PlainMatrixType m_eval(m); + // FIXME if it is really guaranteed that the eigenvalues are already sorted, + // then we don't need to compute a maxCoeff() here, comparing the 1st and last ones is enough. + return ei_sqrt( + (m_eval*m_eval.adjoint()) + .template marked() + .eigenvalues() + .maxCoeff() + ); + } +}; + +/** \eigensolver_module + * + * \returns the matrix norm of this matrix. + */ +template +inline typename NumTraits::Scalar>::Real +MatrixBase::operatorNorm() const +{ + return ei_operatorNorm_selector + ::operatorNorm(derived()); +} + +#ifndef EIGEN_EXTERN_INSTANTIATIONS +template +static void ei_tridiagonal_qr_step(RealScalar* diag, RealScalar* subdiag, int start, int end, Scalar* matrixQ, int n) +{ + RealScalar td = (diag[end-1] - diag[end])*RealScalar(0.5); + RealScalar e2 = ei_abs2(subdiag[end-1]); + RealScalar mu = diag[end] - e2 / (td + (td>0 ? 1 : -1) * ei_sqrt(td*td + e2)); + RealScalar x = diag[start] - mu; + RealScalar z = subdiag[start]; + + for (int k = start; k < end; ++k) + { + PlanarRotation rot; + rot.makeGivens(x, z); + + // do T = G' T G + RealScalar sdk = rot.s() * diag[k] + rot.c() * subdiag[k]; + RealScalar dkp1 = rot.s() * subdiag[k] + rot.c() * diag[k+1]; + + diag[k] = rot.c() * (rot.c() * diag[k] - rot.s() * subdiag[k]) - rot.s() * (rot.c() * subdiag[k] - rot.s() * diag[k+1]); + diag[k+1] = rot.s() * sdk + rot.c() * dkp1; + subdiag[k] = rot.c() * sdk - rot.s() * dkp1; + + if (k > start) + subdiag[k - 1] = rot.c() * subdiag[k-1] - rot.s() * z; + + x = subdiag[k]; + + if (k < end - 1) + { + z = -rot.s() * subdiag[k+1]; + subdiag[k + 1] = rot.c() * subdiag[k+1]; + } + + // apply the givens rotation to the unit matrix Q = Q * G + if (matrixQ) + { + Map > q(matrixQ,n,n); + q.applyOnTheRight(k,k+1,rot); + } + } +} +#endif + +#endif // EIGEN_SELFADJOINTEIGENSOLVER_H diff --git a/Eigen/src/EigenSolver/Tridiagonalization.h b/Eigen/src/EigenSolver/Tridiagonalization.h new file mode 100644 index 000000000..e0bff17b9 --- /dev/null +++ b/Eigen/src/EigenSolver/Tridiagonalization.h @@ -0,0 +1,317 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008 Gael Guennebaud +// +// 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 . + +#ifndef EIGEN_TRIDIAGONALIZATION_H +#define EIGEN_TRIDIAGONALIZATION_H + +/** \ingroup EigenSolver_Module + * \nonstableyet + * + * \class Tridiagonalization + * + * \brief Trigiagonal decomposition of a selfadjoint matrix + * + * \param MatrixType the type of the matrix of which we are performing the tridiagonalization + * + * This class performs a tridiagonal decomposition of a selfadjoint matrix \f$ A \f$ such that: + * \f$ A = Q T Q^* \f$ where \f$ Q \f$ is unitary and \f$ T \f$ a real symmetric tridiagonal matrix. + * + * \sa MatrixBase::tridiagonalize() + */ +template class Tridiagonalization +{ + public: + + typedef _MatrixType MatrixType; + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits::Real RealScalar; + typedef typename ei_packet_traits::type Packet; + + enum { + Size = MatrixType::RowsAtCompileTime, + SizeMinusOne = MatrixType::RowsAtCompileTime==Dynamic + ? Dynamic + : MatrixType::RowsAtCompileTime-1, + PacketSize = ei_packet_traits::size + }; + + typedef Matrix CoeffVectorType; + typedef Matrix DiagonalType; + typedef Matrix SubDiagonalType; + + typedef typename ei_meta_if::IsComplex, + typename NestByValue >::RealReturnType, + Diagonal + >::ret DiagonalReturnType; + + typedef typename ei_meta_if::IsComplex, + typename NestByValue >,0 > >::RealReturnType, + Diagonal< + NestByValue >,0 > + >::ret SubDiagonalReturnType; + + /** This constructor initializes a Tridiagonalization object for + * further use with Tridiagonalization::compute() + */ + Tridiagonalization(int size = Size==Dynamic ? 2 : Size) + : m_matrix(size,size), m_hCoeffs(size-1) + {} + + Tridiagonalization(const MatrixType& matrix) + : m_matrix(matrix), m_hCoeffs(matrix.cols()-1) + { + _compute(m_matrix, m_hCoeffs); + } + + /** Computes or re-compute the tridiagonalization for the matrix \a matrix. + * + * This method allows to re-use the allocated data. + */ + void compute(const MatrixType& matrix) + { + m_matrix = matrix; + m_hCoeffs.resize(matrix.rows()-1, 1); + _compute(m_matrix, m_hCoeffs); + } + + /** \returns the householder coefficients allowing to + * reconstruct the matrix Q from the packed data. + * + * \sa packedMatrix() + */ + inline CoeffVectorType householderCoefficients(void) const { return m_hCoeffs; } + + /** \returns the internal result of the decomposition. + * + * The returned matrix contains the following information: + * - the strict upper part is equal to the input matrix A + * - the diagonal and lower sub-diagonal represent the tridiagonal symmetric matrix (real). + * - 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 transformations: + * 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) ] + * + * See LAPACK for further details on this packed storage. + */ + inline const MatrixType& packedMatrix(void) const { return m_matrix; } + + MatrixType matrixQ() const; + template void matrixQInPlace(MatrixBase* q) const; + MatrixType matrixT() const; + const DiagonalReturnType diagonal(void) const; + const SubDiagonalReturnType subDiagonal(void) const; + + static void decomposeInPlace(MatrixType& mat, DiagonalType& diag, SubDiagonalType& subdiag, bool extractQ = true); + + static void _compute(MatrixType& matA, CoeffVectorType& hCoeffs); + + protected: + + static void _decomposeInPlace3x3(MatrixType& mat, DiagonalType& diag, SubDiagonalType& subdiag, bool extractQ = true); + + MatrixType m_matrix; + CoeffVectorType m_hCoeffs; +}; + +/** \returns an expression of the diagonal vector */ +template +const typename Tridiagonalization::DiagonalReturnType +Tridiagonalization::diagonal(void) const +{ + return m_matrix.diagonal().nestByValue(); +} + +/** \returns an expression of the sub-diagonal vector */ +template +const typename Tridiagonalization::SubDiagonalReturnType +Tridiagonalization::subDiagonal(void) const +{ + int n = m_matrix.rows(); + return Block(m_matrix, 1, 0, n-1,n-1) + .nestByValue().diagonal().nestByValue(); +} + +/** constructs and returns the tridiagonal matrix T. + * Note that the matrix T is equivalent to the diagonal and sub-diagonal of the packed matrix. + * Therefore, it might be often sufficient to directly use the packed matrix, or the vector + * expressions returned by diagonal() and subDiagonal() instead of creating a new matrix. + */ +template +typename Tridiagonalization::MatrixType +Tridiagonalization::matrixT(void) const +{ + // FIXME should this function (and other similar ones) rather take a matrix as argument + // and fill it ? (to avoid temporaries) + int n = m_matrix.rows(); + MatrixType matT = m_matrix; + matT.corner(TopRight,n-1, n-1).diagonal() = subDiagonal().template cast().conjugate(); + if (n>2) + { + matT.corner(TopRight,n-2, n-2).template triangularView().setZero(); + matT.corner(BottomLeft,n-2, n-2).template triangularView().setZero(); + } + return matT; +} + +#ifndef EIGEN_HIDE_HEAVY_CODE + +/** \internal + * Performs a tridiagonal decomposition of \a matA in place. + * + * \param matA the input selfadjoint matrix + * \param hCoeffs returned Householder coefficients + * + * The result is written in the lower triangular part of \a matA. + * + * Implemented from Golub's "Matrix Computations", algorithm 8.3.1. + * + * \sa packedMatrix() + */ +template +void Tridiagonalization::_compute(MatrixType& matA, CoeffVectorType& hCoeffs) +{ + assert(matA.rows()==matA.cols()); + int n = matA.rows(); + Matrix aux(n); + for (int i = 0; i() + * (ei_conj(h) * matA.col(i).end(remainingSize))); + + hCoeffs.end(n-i-1) += (ei_conj(h)*Scalar(-0.5)*(hCoeffs.end(remainingSize).dot(matA.col(i).end(remainingSize)))) * matA.col(i).end(n-i-1); + + matA.corner(BottomRight, remainingSize, remainingSize).template selfadjointView() + .rankUpdate(matA.col(i).end(remainingSize), hCoeffs.end(remainingSize), -1); + + matA.col(i).coeffRef(i+1) = beta; + hCoeffs.coeffRef(i) = h; + } +} + +/** reconstructs and returns the matrix Q */ +template +typename Tridiagonalization::MatrixType +Tridiagonalization::matrixQ(void) const +{ + MatrixType matQ; + matrixQInPlace(&matQ); + return matQ; +} + +template +template +void Tridiagonalization::matrixQInPlace(MatrixBase* q) const +{ + QDerived& matQ = q->derived(); + int n = m_matrix.rows(); + matQ = MatrixType::Identity(n,n); + Matrix aux(n); + for (int i = n-2; i>=0; i--) + { + matQ.corner(BottomRight,n-i-1,n-i-1) + .applyHouseholderOnTheLeft(m_matrix.col(i).end(n-i-2), ei_conj(m_hCoeffs.coeff(i)), &aux.coeffRef(0,0)); + } +} + +/** Performs a full decomposition in place */ +template +void Tridiagonalization::decomposeInPlace(MatrixType& mat, DiagonalType& diag, SubDiagonalType& subdiag, bool extractQ) +{ + int n = mat.rows(); + ei_assert(mat.cols()==n && diag.size()==n && subdiag.size()==n-1); + if (n==3 && (!NumTraits::IsComplex) ) + { + _decomposeInPlace3x3(mat, diag, subdiag, extractQ); + } + else + { + Tridiagonalization tridiag(mat); + diag = tridiag.diagonal(); + subdiag = tridiag.subDiagonal(); + if (extractQ) + tridiag.matrixQInPlace(&mat); + } +} + +/** \internal + * Optimized path for 3x3 matrices. + * Especially useful for plane fitting. + */ +template +void Tridiagonalization::_decomposeInPlace3x3(MatrixType& mat, DiagonalType& diag, SubDiagonalType& subdiag, bool extractQ) +{ + diag[0] = ei_real(mat(0,0)); + RealScalar v1norm2 = ei_abs2(mat(0,2)); + if (ei_isMuchSmallerThan(v1norm2, RealScalar(1))) + { + diag[1] = ei_real(mat(1,1)); + diag[2] = ei_real(mat(2,2)); + subdiag[0] = ei_real(mat(0,1)); + subdiag[1] = ei_real(mat(1,2)); + if (extractQ) + mat.setIdentity(); + } + else + { + RealScalar beta = ei_sqrt(ei_abs2(mat(0,1))+v1norm2); + RealScalar invBeta = RealScalar(1)/beta; + Scalar m01 = mat(0,1) * invBeta; + Scalar m02 = mat(0,2) * invBeta; + Scalar q = RealScalar(2)*m01*mat(1,2) + m02*(mat(2,2) - mat(1,1)); + diag[1] = ei_real(mat(1,1) + m02*q); + diag[2] = ei_real(mat(2,2) - m02*q); + subdiag[0] = beta; + subdiag[1] = ei_real(mat(1,2) - m01 * q); + if (extractQ) + { + mat(0,0) = 1; + mat(0,1) = 0; + mat(0,2) = 0; + mat(1,0) = 0; + mat(1,1) = m01; + mat(1,2) = m02; + mat(2,0) = 0; + mat(2,1) = m02; + mat(2,2) = -m01; + } + } +} + +#endif // EIGEN_HIDE_HEAVY_CODE + +#endif // EIGEN_TRIDIAGONALIZATION_H diff --git a/Eigen/src/QR/ComplexEigenSolver.h b/Eigen/src/QR/ComplexEigenSolver.h deleted file mode 100644 index 6caa6bc2d..000000000 --- a/Eigen/src/QR/ComplexEigenSolver.h +++ /dev/null @@ -1,138 +0,0 @@ -// This file is part of Eigen, a lightweight C++ template library -// for linear algebra. -// -// Copyright (C) 2009 Claire Maurice -// Copyright (C) 2009 Gael Guennebaud -// -// 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 . - -#ifndef EIGEN_COMPLEX_EIGEN_SOLVER_H -#define EIGEN_COMPLEX_EIGEN_SOLVER_H - -#define MAXITER 30 - -template class ComplexEigenSolver -{ - public: - typedef _MatrixType MatrixType; - typedef typename MatrixType::Scalar Scalar; - typedef typename NumTraits::Real RealScalar; - typedef std::complex Complex; - typedef Matrix EigenvalueType; - typedef Matrix EigenvectorType; - - /** - * \brief Default Constructor. - * - * The default constructor is useful in cases in which the user intends to - * perform decompositions via ComplexEigenSolver::compute(const MatrixType&). - */ - ComplexEigenSolver() : m_eivec(), m_eivalues(), m_isInitialized(false) - {} - - ComplexEigenSolver(const MatrixType& matrix) - : m_eivec(matrix.rows(),matrix.cols()), - m_eivalues(matrix.cols()), - m_isInitialized(false) - { - compute(matrix); - } - - EigenvectorType eigenvectors(void) const - { - ei_assert(m_isInitialized && "ComplexEigenSolver is not initialized."); - return m_eivec; - } - - EigenvalueType eigenvalues() const - { - ei_assert(m_isInitialized && "ComplexEigenSolver is not initialized."); - return m_eivalues; - } - - void compute(const MatrixType& matrix); - - protected: - MatrixType m_eivec; - EigenvalueType m_eivalues; - bool m_isInitialized; -}; - - -template -void ComplexEigenSolver::compute(const MatrixType& matrix) -{ - assert(matrix.cols() == matrix.rows()); - int n = matrix.cols(); - m_eivalues.resize(n,1); - - RealScalar eps = epsilon(); - - // Reduce to complex Schur form - ComplexSchur schur(matrix); - - m_eivalues = schur.matrixT().diagonal(); - - m_eivec.setZero(); - - Scalar d2, z; - RealScalar norm = matrix.norm(); - - // compute the (normalized) eigenvectors - for(int k=n-1 ; k>=0 ; k--) - { - d2 = schur.matrixT().coeff(k,k); - m_eivec.coeffRef(k,k) = Scalar(1.0,0.0); - for(int i=k-1 ; i>=0 ; i--) - { - m_eivec.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; - - } - m_eivec.col(k).normalize(); - } - - m_eivec = schur.matrixU() * m_eivec; - m_isInitialized = true; - - // sort the eigenvalues - { - for (int i=0; i -// -// 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 . - -#ifndef EIGEN_COMPLEX_SCHUR_H -#define EIGEN_COMPLEX_SCHUR_H - -#define MAXITER 30 - -/** \ingroup QR - * - * \class ComplexShur - * - * \brief Performs a complex Shur decomposition of a real or complex square matrix - * - */ -template class ComplexSchur -{ - public: - typedef _MatrixType MatrixType; - typedef typename MatrixType::Scalar Scalar; - typedef typename NumTraits::Real RealScalar; - typedef std::complex Complex; - typedef Matrix ComplexMatrixType; - - /** - * \brief Default Constructor. - * - * The default constructor is useful in cases in which the user intends to - * perform decompositions via ComplexSchur::compute(const MatrixType&). - */ - ComplexSchur() : m_matT(), m_matU(), m_isInitialized(false) - {} - - ComplexSchur(const MatrixType& matrix) - : m_matT(matrix.rows(),matrix.cols()), - m_matU(matrix.rows(),matrix.cols()), - m_isInitialized(false) - { - compute(matrix); - } - - ComplexMatrixType matrixU() const - { - ei_assert(m_isInitialized && "ComplexSchur is not initialized."); - return m_matU; - } - - ComplexMatrixType matrixT() const - { - ei_assert(m_isInitialized && "ComplexShur is not initialized."); - return m_matT; - } - - void compute(const MatrixType& matrix); - - protected: - ComplexMatrixType m_matT, m_matU; - bool m_isInitialized; -}; - -/** Computes the principal value of the square root of the complex \a z. */ -template -std::complex ei_sqrt(const std::complex &z) -{ - RealScalar t, tre, tim; - - t = ei_abs(z); - - if (ei_abs(ei_real(z)) <= ei_abs(ei_real(z))) - { - // No cancellation in these formulas - tre = ei_sqrt(0.5*(t + ei_real(z))); - tim = ei_sqrt(0.5*(t - ei_real(z))); - } - else - { - // Stable computation of the above formulas - if (z.real() > 0) - { - tre = t + z.real(); - tim = ei_abs(ei_imag(z))*ei_sqrt(0.5/tre); - tre = ei_sqrt(0.5*tre); - } - else - { - tim = t - z.real(); - tre = ei_abs(ei_imag(z))*ei_sqrt(0.5/tim); - tim = ei_sqrt(0.5*tim); - } - } - if(z.imag() < 0) - tim = -tim; - - return (std::complex(tre,tim)); - -} - -template -void ComplexSchur::compute(const MatrixType& matrix) -{ - assert(matrix.cols() == matrix.rows()); - int n = matrix.cols(); - - // Reduce to Hessenberg form - HessenbergDecomposition hess(matrix); - - m_matT = hess.matrixH(); - m_matU = hess.matrixQ(); - - int iu = m_matT.cols() - 1; - int il; - RealScalar d,sd,sf; - Complex c,b,disc,r1,r2,kappa; - - RealScalar eps = epsilon(); - - int iter = 0; - while(true) - { - //locate the range in which to iterate - while(iu > 0) - { - d = ei_norm1(m_matT.coeffRef(iu,iu)) + ei_norm1(m_matT.coeffRef(iu-1,iu-1)); - sd = ei_norm1(m_matT.coeffRef(iu,iu-1)); - - if(sd >= eps * d) break; // FIXME : precision criterion ?? - - m_matT.coeffRef(iu,iu-1) = Complex(0); - iter = 0; - --iu; - } - if(iu==0) break; - iter++; - - if(iter >= MAXITER) - { - // FIXME : what to do when iter==MAXITER ?? - std::cerr << "MAXITER" << std::endl; - return; - } - - il = iu-1; - while( il > 0 ) - { - // check if the current 2x2 block on the diagonal is upper triangular - d = ei_norm1(m_matT.coeffRef(il,il)) + ei_norm1(m_matT.coeffRef(il-1,il-1)); - sd = ei_norm1(m_matT.coeffRef(il,il-1)); - - if(sd < eps * d) break; // FIXME : precision criterion ?? - - --il; - } - - if( il != 0 ) m_matT.coeffRef(il,il-1) = Complex(0); - - // compute the shift (the normalization by sf is to avoid under/overflow) - Matrix t = m_matT.template block<2,2>(iu-1,iu-1); - sf = t.cwise().abs().sum(); - t /= sf; - - c = t.determinant(); - b = t.diagonal().sum(); - - disc = ei_sqrt(b*b - RealScalar(4)*c); - - 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; - - // perform the QR step using Givens rotations - PlanarRotation rot; - rot.makeGivens(m_matT.coeff(il,il) - kappa, m_matT.coeff(il+1,il)); - - for(int i=il ; i -// -// 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 . - -#ifndef EIGEN_EIGENSOLVER_H -#define EIGEN_EIGENSOLVER_H - -/** \ingroup QR_Module - * \nonstableyet - * - * \class EigenSolver - * - * \brief Eigen values/vectors solver for non selfadjoint matrices - * - * \param MatrixType the type of the matrix of which we are computing the eigen decomposition - * - * Currently it only support real matrices. - * - * \note this code was adapted from JAMA (public domain) - * - * \sa MatrixBase::eigenvalues(), SelfAdjointEigenSolver - */ -template class EigenSolver -{ - public: - - typedef _MatrixType MatrixType; - typedef typename MatrixType::Scalar Scalar; - typedef typename NumTraits::Real RealScalar; - typedef std::complex Complex; - typedef Matrix EigenvalueType; - typedef Matrix EigenvectorType; - typedef Matrix RealVectorType; - typedef Matrix RealVectorTypeX; - - /** - * \brief Default Constructor. - * - * The default constructor is useful in cases in which the user intends to - * perform decompositions via EigenSolver::compute(const MatrixType&). - */ - EigenSolver() : m_eivec(), m_eivalues(), m_isInitialized(false) {} - - EigenSolver(const MatrixType& matrix) - : m_eivec(matrix.rows(), matrix.cols()), - m_eivalues(matrix.cols()), - m_isInitialized(false) - { - compute(matrix); - } - - - EigenvectorType eigenvectors(void) const; - - /** \returns a real matrix V of pseudo eigenvectors. - * - * 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. - * - * 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$ - * - * \sa pseudoEigenvalueMatrix() - */ - const MatrixType& pseudoEigenvectors() const - { - ei_assert(m_isInitialized && "EigenSolver is not initialized."); - return m_eivec; - } - - MatrixType pseudoEigenvalueMatrix() const; - - /** \returns the eigenvalues as a column vector */ - EigenvalueType eigenvalues() const - { - ei_assert(m_isInitialized && "EigenSolver is not initialized."); - return m_eivalues; - } - - EigenSolver& compute(const MatrixType& matrix); - - private: - - void orthes(MatrixType& matH, RealVectorType& ort); - void hqr2(MatrixType& matH); - - protected: - MatrixType m_eivec; - EigenvalueType m_eivalues; - bool m_isInitialized; -}; - -/** \returns the real block diagonal matrix D of the eigenvalues. - * - * See pseudoEigenvectors() for the details. - */ -template -MatrixType EigenSolver::pseudoEigenvalueMatrix() const -{ - ei_assert(m_isInitialized && "EigenSolver is not initialized."); - int n = m_eivec.cols(); - MatrixType matD = MatrixType::Zero(n,n); - for (int i=0; i(i,i) << ei_real(m_eivalues.coeff(i)), ei_imag(m_eivalues.coeff(i)), - -ei_imag(m_eivalues.coeff(i)), ei_real(m_eivalues.coeff(i)); - ++i; - } - } - return matD; -} - -/** \returns the normalized complex eigenvectors as a matrix of column vectors. - * - * \sa eigenvalues(), pseudoEigenvectors() - */ -template -typename EigenSolver::EigenvectorType EigenSolver::eigenvectors(void) const -{ - ei_assert(m_isInitialized && "EigenSolver is not initialized."); - int n = m_eivec.cols(); - EigenvectorType matV(n,n); - for (int j=0; j(); - } - else - { - // we have a pair of complex eigen values - for (int i=0; i -EigenSolver& EigenSolver::compute(const MatrixType& matrix) -{ - assert(matrix.cols() == matrix.rows()); - int n = matrix.cols(); - m_eivalues.resize(n,1); - - MatrixType matH = matrix; - RealVectorType ort(n); - - // Reduce to Hessenberg form. - orthes(matH, ort); - - // Reduce Hessenberg to real Schur form. - hqr2(matH); - - m_isInitialized = true; - return *this; -} - -// Nonsymmetric reduction to Hessenberg form. -template -void EigenSolver::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).cwise().abs().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 -std::complex cdiv(Scalar xr, Scalar xi, Scalar yr, Scalar yi) -{ - Scalar r,d; - if (ei_abs(yr) > ei_abs(yi)) - { - r = yi/yr; - d = yr + r*yi; - return std::complex((xr + r*xi)/d, (xi - r*xr)/d); - } - else - { - r = yr/yi; - d = yi + r*yr; - return std::complex((r*xr + xi)/d, (r*xi - xr)/d); - } -} - - -// Nonsymmetric reduction from Hessenberg to real Schur form. -template -void EigenSolver::hqr2(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. - - // 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::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().cwise().abs().sum() + matH.corner(BottomLeft,n,n).diagonal().cwise().abs().sum(); - 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)).cwise().abs().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--) - { - p = m_eivalues.coeff(n).real(); - q = m_eivalues.coeff(n).imag(); - - // Scalar vector - if (q == 0) - { - int l = n; - matH.coeffRef(n,n) = 1.0; - for (int i = n-1; i >= 0; i--) - { - w = matH.coeff(i,i) - p; - r = matH.row(i).segment(l,n-l+1).dot(matH.col(n).segment(l, n-l+1)); - - if (m_eivalues.coeff(i).imag() < 0.0) - { - z = w; - s = r; - } - else - { - l = i; - if (m_eivalues.coeff(i).imag() == 0.0) - { - if (w != 0.0) - matH.coeffRef(i,n) = -r / w; - else - matH.coeffRef(i,n) = -r / (eps * norm); - } - else // Solve real equations - { - x = matH.coeff(i,i+1); - y = matH.coeff(i+1,i); - q = (m_eivalues.coeff(i).real() - p) * (m_eivalues.coeff(i).real() - p) + m_eivalues.coeff(i).imag() * m_eivalues.coeff(i).imag(); - t = (x * s - z * r) / q; - matH.coeffRef(i,n) = t; - if (ei_abs(x) > ei_abs(z)) - matH.coeffRef(i+1,n) = (-r - w * t) / x; - else - matH.coeffRef(i+1,n) = (-s - y * t) / z; - } - - // Overflow control - t = ei_abs(matH.coeff(i,n)); - if ((eps * t) * t > 1) - matH.col(n).end(nn-i) /= t; - } - } - } - else if (q < 0) // Complex vector - { - std::complex cc; - int l = n-1; - - // Last vector component imaginary so matrix is triangular - if (ei_abs(matH.coeff(n,n-1)) > ei_abs(matH.coeff(n-1,n))) - { - matH.coeffRef(n-1,n-1) = q / matH.coeff(n,n-1); - matH.coeffRef(n-1,n) = -(matH.coeff(n,n) - p) / matH.coeff(n,n-1); - } - else - { - cc = cdiv(0.0,-matH.coeff(n-1,n),matH.coeff(n-1,n-1)-p,q); - matH.coeffRef(n-1,n-1) = ei_real(cc); - matH.coeffRef(n-1,n) = ei_imag(cc); - } - matH.coeffRef(n,n-1) = 0.0; - matH.coeffRef(n,n) = 1.0; - for (int i = n-2; i >= 0; i--) - { - Scalar ra,sa,vr,vi; - ra = matH.row(i).segment(l, n-l+1).dot(matH.col(n-1).segment(l, n-l+1)); - sa = matH.row(i).segment(l, n-l+1).dot(matH.col(n).segment(l, n-l+1)); - w = matH.coeff(i,i) - p; - - if (m_eivalues.coeff(i).imag() < 0.0) - { - z = w; - r = ra; - s = sa; - } - else - { - l = i; - if (m_eivalues.coeff(i).imag() == 0) - { - cc = cdiv(-ra,-sa,w,q); - matH.coeffRef(i,n-1) = ei_real(cc); - matH.coeffRef(i,n) = ei_imag(cc); - } - else - { - // Solve complex equations - x = matH.coeff(i,i+1); - y = matH.coeff(i+1,i); - vr = (m_eivalues.coeff(i).real() - p) * (m_eivalues.coeff(i).real() - p) + m_eivalues.coeff(i).imag() * m_eivalues.coeff(i).imag() - q * q; - vi = (m_eivalues.coeff(i).real() - p) * Scalar(2) * q; - if ((vr == 0.0) && (vi == 0.0)) - vr = eps * norm * (ei_abs(w) + ei_abs(q) + ei_abs(x) + ei_abs(y) + ei_abs(z)); - - cc= cdiv(x*r-z*ra+q*sa,x*s-z*sa-q*ra,vr,vi); - matH.coeffRef(i,n-1) = ei_real(cc); - matH.coeffRef(i,n) = ei_imag(cc); - if (ei_abs(x) > (ei_abs(z) + ei_abs(q))) - { - matH.coeffRef(i+1,n-1) = (-ra - w * matH.coeff(i,n-1) + q * matH.coeff(i,n)) / x; - matH.coeffRef(i+1,n) = (-sa - w * matH.coeff(i,n) - q * matH.coeff(i,n-1)) / x; - } - else - { - cc = cdiv(-r-y*matH.coeff(i,n-1),-s-y*matH.coeff(i,n),z,q); - matH.coeffRef(i+1,n-1) = ei_real(cc); - matH.coeffRef(i+1,n) = ei_imag(cc); - } - } - - // Overflow control - t = std::max(ei_abs(matH.coeff(i,n-1)),ei_abs(matH.coeff(i,n))); - if ((eps * t) * t > 1) - matH.block(i, n-1, nn-i, 2) /= t; - - } - } - } - } - - // Vectors of isolated roots - for (int i = 0; i < nn; ++i) - { - // FIXME again what's the purpose of this test ? - // in this algo low==0 and high==nn-1 !! - if (i < low || i > high) - { - m_eivec.row(i).end(nn-i) = matH.row(i).end(nn-i); - } - } - - // Back transformation to get eigenvectors of original matrix - int bRows = high-low+1; - for (int j = nn-1; j >= low; j--) - { - int bSize = std::min(j,high)-low+1; - m_eivec.col(j).segment(low, bRows) = (m_eivec.block(low, low, bRows, bSize) * matH.col(j).segment(low, bSize)); - } -} - -#endif // EIGEN_EIGENSOLVER_H diff --git a/Eigen/src/QR/HessenbergDecomposition.h b/Eigen/src/QR/HessenbergDecomposition.h deleted file mode 100644 index 6b261a8a7..000000000 --- a/Eigen/src/QR/HessenbergDecomposition.h +++ /dev/null @@ -1,206 +0,0 @@ -// This file is part of Eigen, a lightweight C++ template library -// for linear algebra. -// -// Copyright (C) 2008 Gael Guennebaud -// -// 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 . - -#ifndef EIGEN_HESSENBERGDECOMPOSITION_H -#define EIGEN_HESSENBERGDECOMPOSITION_H - -/** \ingroup QR_Module - * \nonstableyet - * - * \class HessenbergDecomposition - * - * \brief Reduces a squared matrix to an Hessemberg form - * - * \param 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. - * - * \sa class Tridiagonalization, class Qr - */ -template class HessenbergDecomposition -{ - public: - - typedef _MatrixType MatrixType; - typedef typename MatrixType::Scalar Scalar; - typedef typename NumTraits::Real RealScalar; - - enum { - Size = MatrixType::RowsAtCompileTime, - SizeMinusOne = MatrixType::RowsAtCompileTime==Dynamic - ? Dynamic - : MatrixType::RowsAtCompileTime-1 - }; - - typedef Matrix CoeffVectorType; - typedef Matrix DiagonalType; - typedef Matrix SubDiagonalType; - - typedef typename NestByValue >::RealReturnType DiagonalReturnType; - - typedef typename NestByValue >,0 > >::RealReturnType SubDiagonalReturnType; - - /** This constructor initializes a HessenbergDecomposition object for - * further use with HessenbergDecomposition::compute() - */ - HessenbergDecomposition(int size = Size==Dynamic ? 2 : Size) - : m_matrix(size,size), m_hCoeffs(size-1) - {} - - HessenbergDecomposition(const MatrixType& matrix) - : m_matrix(matrix), - m_hCoeffs(matrix.cols()-1) - { - _compute(m_matrix, m_hCoeffs); - } - - /** Computes or re-compute the Hessenberg decomposition for the matrix \a matrix. - * - * This method allows to re-use the allocated data. - */ - void compute(const MatrixType& matrix) - { - m_matrix = matrix; - m_hCoeffs.resize(matrix.rows()-1,1); - _compute(m_matrix, m_hCoeffs); - } - - /** \returns the householder coefficients allowing to - * reconstruct the matrix Q from the packed data. - * - * \sa packedMatrix() - */ - CoeffVectorType householderCoefficients() const { return m_hCoeffs; } - - /** \returns the internal result of the decomposition. - * - * 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) ] - * - * See LAPACK for further details on this packed storage. - */ - const MatrixType& packedMatrix(void) const { return m_matrix; } - - MatrixType matrixQ() const; - MatrixType matrixH() const; - - private: - - static void _compute(MatrixType& matA, CoeffVectorType& hCoeffs); - - protected: - MatrixType m_matrix; - CoeffVectorType m_hCoeffs; -}; - -#ifndef EIGEN_HIDE_HEAVY_CODE - -/** \internal - * Performs a tridiagonal decomposition of \a matA in place. - * - * \param matA the input selfadjoint matrix - * \param hCoeffs returned Householder coefficients - * - * The result is written in the lower triangular part of \a matA. - * - * Implemented from Golub's "Matrix Computations", algorithm 8.3.1. - * - * \sa packedMatrix() - */ -template -void HessenbergDecomposition::_compute(MatrixType& matA, CoeffVectorType& hCoeffs) -{ - assert(matA.rows()==matA.cols()); - int n = matA.rows(); - Matrix temp(n); - for (int i = 0; i -typename HessenbergDecomposition::MatrixType -HessenbergDecomposition::matrixQ() const -{ - int n = m_matrix.rows(); - MatrixType matQ = MatrixType::Identity(n,n); - Matrix temp(n); - for (int i = n-2; i>=0; i--) - { - matQ.corner(BottomRight,n-i-1,n-i-1) - .applyHouseholderOnTheLeft(m_matrix.col(i).end(n-i-2), ei_conj(m_hCoeffs.coeff(i)), &temp.coeffRef(0,0)); - } - return matQ; -} - -#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 HessenbergDecomposition::MatrixType -HessenbergDecomposition::matrixH() const -{ - // FIXME should this function (and other similar) rather take a matrix as argument - // and fill it (to avoid temporaries) - int n = m_matrix.rows(); - MatrixType matH = m_matrix; - if (n>2) - matH.corner(BottomLeft,n-2, n-2).template triangularView().setZero(); - return matH; -} - -#endif // EIGEN_HESSENBERGDECOMPOSITION_H diff --git a/Eigen/src/QR/QrInstantiations.cpp b/Eigen/src/QR/QrInstantiations.cpp index 38b0a57da..695377d69 100644 --- a/Eigen/src/QR/QrInstantiations.cpp +++ b/Eigen/src/QR/QrInstantiations.cpp @@ -33,11 +33,6 @@ namespace Eigen { -template static void ei_tridiagonal_qr_step(float* , float* , int, int, float* , int); -template static void ei_tridiagonal_qr_step(double* , double* , int, int, double* , int); -template static void ei_tridiagonal_qr_step(float* , float* , int, int, std::complex* , int); -template static void ei_tridiagonal_qr_step(double* , double* , int, int, std::complex* , int); - EIGEN_QR_MODULE_INSTANTIATE(); } diff --git a/Eigen/src/QR/SelfAdjointEigenSolver.h b/Eigen/src/QR/SelfAdjointEigenSolver.h deleted file mode 100644 index 580b042f6..000000000 --- a/Eigen/src/QR/SelfAdjointEigenSolver.h +++ /dev/null @@ -1,366 +0,0 @@ -// This file is part of Eigen, a lightweight C++ template library -// for linear algebra. -// -// Copyright (C) 2008 Gael Guennebaud -// -// 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 . - -#ifndef EIGEN_SELFADJOINTEIGENSOLVER_H -#define EIGEN_SELFADJOINTEIGENSOLVER_H - -/** \qr_module \ingroup QR_Module - * \nonstableyet - * - * \class SelfAdjointEigenSolver - * - * \brief Eigen values/vectors solver for selfadjoint matrix - * - * \param MatrixType the type of the matrix of which we are computing the eigen decomposition - * - * \note MatrixType must be an actual Matrix type, it can't be an expression type. - * - * \sa MatrixBase::eigenvalues(), class EigenSolver - */ -template class SelfAdjointEigenSolver -{ - public: - - enum {Size = _MatrixType::RowsAtCompileTime }; - typedef _MatrixType MatrixType; - typedef typename MatrixType::Scalar Scalar; - typedef typename NumTraits::Real RealScalar; - typedef std::complex Complex; - typedef Matrix RealVectorType; - typedef Matrix RealVectorTypeX; - typedef Tridiagonalization TridiagonalizationType; -// typedef typename TridiagonalizationType::TridiagonalMatrixType TridiagonalMatrixType; - - SelfAdjointEigenSolver() - : m_eivec(int(Size), int(Size)), - m_eivalues(int(Size)) - { - ei_assert(Size!=Dynamic); - } - - SelfAdjointEigenSolver(int size) - : m_eivec(size, size), - m_eivalues(size) - {} - - /** Constructors computing the eigenvalues of the selfadjoint matrix \a matrix, - * as well as the eigenvectors if \a computeEigenvectors is true. - * - * \sa compute(MatrixType,bool), SelfAdjointEigenSolver(MatrixType,MatrixType,bool) - */ - SelfAdjointEigenSolver(const MatrixType& matrix, bool computeEigenvectors = true) - : m_eivec(matrix.rows(), matrix.cols()), - m_eivalues(matrix.cols()) - { - compute(matrix, computeEigenvectors); - } - - /** Constructors computing the eigenvalues of the generalized eigen problem - * \f$ Ax = lambda B x \f$ with \a matA the selfadjoint matrix \f$ A \f$ - * and \a matB the positive definite matrix \f$ B \f$ . The eigenvectors - * are computed if \a computeEigenvectors is true. - * - * \sa compute(MatrixType,MatrixType,bool), SelfAdjointEigenSolver(MatrixType,bool) - */ - SelfAdjointEigenSolver(const MatrixType& matA, const MatrixType& matB, bool computeEigenvectors = true) - : m_eivec(matA.rows(), matA.cols()), - m_eivalues(matA.cols()) - { - compute(matA, matB, computeEigenvectors); - } - - SelfAdjointEigenSolver& compute(const MatrixType& matrix, bool computeEigenvectors = true); - - SelfAdjointEigenSolver& compute(const MatrixType& matA, const MatrixType& matB, bool computeEigenvectors = true); - - /** \returns the computed eigen vectors as a matrix of column vectors */ - MatrixType eigenvectors(void) const - { - #ifndef NDEBUG - ei_assert(m_eigenvectorsOk); - #endif - return m_eivec; - } - - /** \returns the computed eigen values */ - RealVectorType eigenvalues(void) const { return m_eivalues; } - - /** \returns the positive square root of the matrix - * - * \note the matrix itself must be positive in order for this to make sense. - */ - MatrixType operatorSqrt() const - { - return m_eivec * m_eivalues.cwise().sqrt().asDiagonal() * m_eivec.adjoint(); - } - - /** \returns the positive inverse square root of the matrix - * - * \note the matrix itself must be positive definite in order for this to make sense. - */ - MatrixType operatorInverseSqrt() const - { - return m_eivec * m_eivalues.cwise().inverse().cwise().sqrt().asDiagonal() * m_eivec.adjoint(); - } - - - protected: - MatrixType m_eivec; - RealVectorType m_eivalues; - #ifndef NDEBUG - bool m_eigenvectorsOk; - #endif -}; - -#ifndef EIGEN_HIDE_HEAVY_CODE - -/** \internal - * - * \qr_module - * - * Performs a QR step on a tridiagonal symmetric matrix represented as a - * pair of two vectors \a diag and \a subdiag. - * - * \param matA the input selfadjoint matrix - * \param hCoeffs returned Householder coefficients - * - * For compilation efficiency reasons, this procedure does not use eigen expression - * for its arguments. - * - * Implemented from Golub's "Matrix Computations", algorithm 8.3.2: - * "implicit symmetric QR step with Wilkinson shift" - */ -template -static void ei_tridiagonal_qr_step(RealScalar* diag, RealScalar* subdiag, int start, int end, Scalar* matrixQ, int n); - -/** Computes the eigenvalues of the selfadjoint matrix \a matrix, - * as well as the eigenvectors if \a computeEigenvectors is true. - * - * \sa SelfAdjointEigenSolver(MatrixType,bool), compute(MatrixType,MatrixType,bool) - */ -template -SelfAdjointEigenSolver& SelfAdjointEigenSolver::compute(const MatrixType& matrix, bool computeEigenvectors) -{ - #ifndef NDEBUG - m_eigenvectorsOk = computeEigenvectors; - #endif - assert(matrix.cols() == matrix.rows()); - int n = matrix.cols(); - m_eivalues.resize(n,1); - - if(n==1) - { - m_eivalues.coeffRef(0,0) = ei_real(matrix.coeff(0,0)); - m_eivec.setOnes(); - return *this; - } - - m_eivec = matrix; - - // FIXME, should tridiag be a local variable of this function or an attribute of SelfAdjointEigenSolver ? - // the latter avoids multiple memory allocation when the same SelfAdjointEigenSolver is used multiple times... - // (same for diag and subdiag) - RealVectorType& diag = m_eivalues; - typename TridiagonalizationType::SubDiagonalType subdiag(n-1); - TridiagonalizationType::decomposeInPlace(m_eivec, diag, subdiag, computeEigenvectors); - - int end = n-1; - int start = 0; - while (end>0) - { - for (int i = start; i0 && subdiag[end-1]==0) - end--; - if (end<=0) - break; - start = end - 1; - while (start>0 && subdiag[start-1]!=0) - start--; - - ei_tridiagonal_qr_step(diag.data(), subdiag.data(), start, end, computeEigenvectors ? m_eivec.data() : (Scalar*)0, n); - } - - // Sort eigenvalues and corresponding vectors. - // TODO make the sort optional ? - // TODO use a better sort algorithm !! - for (int i = 0; i < n-1; ++i) - { - int k; - m_eivalues.segment(i,n-i).minCoeff(&k); - if (k > 0) - { - std::swap(m_eivalues[i], m_eivalues[k+i]); - m_eivec.col(i).swap(m_eivec.col(k+i)); - } - } - return *this; -} - -/** Computes the eigenvalues of the generalized eigen problem - * \f$ Ax = lambda B x \f$ with \a matA the selfadjoint matrix \f$ A \f$ - * and \a matB the positive definite matrix \f$ B \f$ . The eigenvectors - * are computed if \a computeEigenvectors is true. - * - * \sa SelfAdjointEigenSolver(MatrixType,MatrixType,bool), compute(MatrixType,bool) - */ -template -SelfAdjointEigenSolver& SelfAdjointEigenSolver:: -compute(const MatrixType& matA, const MatrixType& matB, bool computeEigenvectors) -{ - ei_assert(matA.cols()==matA.rows() && matB.rows()==matA.rows() && matB.cols()==matB.rows()); - - // Compute the cholesky decomposition of matB = L L' - LLT cholB(matB); - - // compute C = inv(L) A inv(L') - MatrixType matC = matA; - cholB.matrixL().solveInPlace(matC); - // FIXME since we currently do not support A * inv(L'), let's do (inv(L) A')' : - matC.adjointInPlace(); - cholB.matrixL().solveInPlace(matC); - matC.adjointInPlace(); - // this version works too: -// matC = matC.transpose(); -// cholB.matrixL().conjugate().template marked().solveTriangularInPlace(matC); -// matC = matC.transpose(); - // FIXME: this should work: (currently it only does for small matrices) -// Transpose trMatC(matC); -// cholB.matrixL().conjugate().eval().template marked().solveTriangularInPlace(trMatC); - - compute(matC, computeEigenvectors); - - if (computeEigenvectors) - { - // transform back the eigen vectors: evecs = inv(U) * evecs - cholB.matrixU().solveInPlace(m_eivec); - for (int i=0; i -inline Matrix::Scalar>::Real, ei_traits::ColsAtCompileTime, 1> -MatrixBase::eigenvalues() const -{ - ei_assert(Flags&SelfAdjointBit); - return SelfAdjointEigenSolver(eval(),false).eigenvalues(); -} - -template -struct ei_operatorNorm_selector -{ - static inline typename NumTraits::Scalar>::Real - operatorNorm(const MatrixBase& m) - { - // FIXME if it is really guaranteed that the eigenvalues are already sorted, - // then we don't need to compute a maxCoeff() here, comparing the 1st and last ones is enough. - return m.eigenvalues().cwise().abs().maxCoeff(); - } -}; - -template struct ei_operatorNorm_selector -{ - static inline typename NumTraits::Scalar>::Real - operatorNorm(const MatrixBase& m) - { - typename Derived::PlainMatrixType m_eval(m); - // FIXME if it is really guaranteed that the eigenvalues are already sorted, - // then we don't need to compute a maxCoeff() here, comparing the 1st and last ones is enough. - return ei_sqrt( - (m_eval*m_eval.adjoint()) - .template marked() - .eigenvalues() - .maxCoeff() - ); - } -}; - -/** \qr_module - * - * \returns the matrix norm of this matrix. - */ -template -inline typename NumTraits::Scalar>::Real -MatrixBase::operatorNorm() const -{ - return ei_operatorNorm_selector - ::operatorNorm(derived()); -} - -#ifndef EIGEN_EXTERN_INSTANTIATIONS -template -static void ei_tridiagonal_qr_step(RealScalar* diag, RealScalar* subdiag, int start, int end, Scalar* matrixQ, int n) -{ - RealScalar td = (diag[end-1] - diag[end])*RealScalar(0.5); - RealScalar e2 = ei_abs2(subdiag[end-1]); - RealScalar mu = diag[end] - e2 / (td + (td>0 ? 1 : -1) * ei_sqrt(td*td + e2)); - RealScalar x = diag[start] - mu; - RealScalar z = subdiag[start]; - - for (int k = start; k < end; ++k) - { - PlanarRotation rot; - rot.makeGivens(x, z); - - // do T = G' T G - RealScalar sdk = rot.s() * diag[k] + rot.c() * subdiag[k]; - RealScalar dkp1 = rot.s() * subdiag[k] + rot.c() * diag[k+1]; - - diag[k] = rot.c() * (rot.c() * diag[k] - rot.s() * subdiag[k]) - rot.s() * (rot.c() * subdiag[k] - rot.s() * diag[k+1]); - diag[k+1] = rot.s() * sdk + rot.c() * dkp1; - subdiag[k] = rot.c() * sdk - rot.s() * dkp1; - - if (k > start) - subdiag[k - 1] = rot.c() * subdiag[k-1] - rot.s() * z; - - x = subdiag[k]; - - if (k < end - 1) - { - z = -rot.s() * subdiag[k+1]; - subdiag[k + 1] = rot.c() * subdiag[k+1]; - } - - // apply the givens rotation to the unit matrix Q = Q * G - if (matrixQ) - { - Map > q(matrixQ,n,n); - q.applyOnTheRight(k,k+1,rot); - } - } -} -#endif - -#endif // EIGEN_SELFADJOINTEIGENSOLVER_H diff --git a/Eigen/src/QR/Tridiagonalization.h b/Eigen/src/QR/Tridiagonalization.h deleted file mode 100644 index 2053505f6..000000000 --- a/Eigen/src/QR/Tridiagonalization.h +++ /dev/null @@ -1,317 +0,0 @@ -// This file is part of Eigen, a lightweight C++ template library -// for linear algebra. -// -// Copyright (C) 2008 Gael Guennebaud -// -// 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 . - -#ifndef EIGEN_TRIDIAGONALIZATION_H -#define EIGEN_TRIDIAGONALIZATION_H - -/** \ingroup QR_Module - * \nonstableyet - * - * \class Tridiagonalization - * - * \brief Trigiagonal decomposition of a selfadjoint matrix - * - * \param MatrixType the type of the matrix of which we are performing the tridiagonalization - * - * This class performs a tridiagonal decomposition of a selfadjoint matrix \f$ A \f$ such that: - * \f$ A = Q T Q^* \f$ where \f$ Q \f$ is unitary and \f$ T \f$ a real symmetric tridiagonal matrix. - * - * \sa MatrixBase::tridiagonalize() - */ -template class Tridiagonalization -{ - public: - - typedef _MatrixType MatrixType; - typedef typename MatrixType::Scalar Scalar; - typedef typename NumTraits::Real RealScalar; - typedef typename ei_packet_traits::type Packet; - - enum { - Size = MatrixType::RowsAtCompileTime, - SizeMinusOne = MatrixType::RowsAtCompileTime==Dynamic - ? Dynamic - : MatrixType::RowsAtCompileTime-1, - PacketSize = ei_packet_traits::size - }; - - typedef Matrix CoeffVectorType; - typedef Matrix DiagonalType; - typedef Matrix SubDiagonalType; - - typedef typename ei_meta_if::IsComplex, - typename NestByValue >::RealReturnType, - Diagonal - >::ret DiagonalReturnType; - - typedef typename ei_meta_if::IsComplex, - typename NestByValue >,0 > >::RealReturnType, - Diagonal< - NestByValue >,0 > - >::ret SubDiagonalReturnType; - - /** This constructor initializes a Tridiagonalization object for - * further use with Tridiagonalization::compute() - */ - Tridiagonalization(int size = Size==Dynamic ? 2 : Size) - : m_matrix(size,size), m_hCoeffs(size-1) - {} - - Tridiagonalization(const MatrixType& matrix) - : m_matrix(matrix), m_hCoeffs(matrix.cols()-1) - { - _compute(m_matrix, m_hCoeffs); - } - - /** Computes or re-compute the tridiagonalization for the matrix \a matrix. - * - * This method allows to re-use the allocated data. - */ - void compute(const MatrixType& matrix) - { - m_matrix = matrix; - m_hCoeffs.resize(matrix.rows()-1, 1); - _compute(m_matrix, m_hCoeffs); - } - - /** \returns the householder coefficients allowing to - * reconstruct the matrix Q from the packed data. - * - * \sa packedMatrix() - */ - inline CoeffVectorType householderCoefficients(void) const { return m_hCoeffs; } - - /** \returns the internal result of the decomposition. - * - * The returned matrix contains the following information: - * - the strict upper part is equal to the input matrix A - * - the diagonal and lower sub-diagonal represent the tridiagonal symmetric matrix (real). - * - 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 transformations: - * 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) ] - * - * See LAPACK for further details on this packed storage. - */ - inline const MatrixType& packedMatrix(void) const { return m_matrix; } - - MatrixType matrixQ() const; - template void matrixQInPlace(MatrixBase* q) const; - MatrixType matrixT() const; - const DiagonalReturnType diagonal(void) const; - const SubDiagonalReturnType subDiagonal(void) const; - - static void decomposeInPlace(MatrixType& mat, DiagonalType& diag, SubDiagonalType& subdiag, bool extractQ = true); - - static void _compute(MatrixType& matA, CoeffVectorType& hCoeffs); - - protected: - - static void _decomposeInPlace3x3(MatrixType& mat, DiagonalType& diag, SubDiagonalType& subdiag, bool extractQ = true); - - MatrixType m_matrix; - CoeffVectorType m_hCoeffs; -}; - -/** \returns an expression of the diagonal vector */ -template -const typename Tridiagonalization::DiagonalReturnType -Tridiagonalization::diagonal(void) const -{ - return m_matrix.diagonal().nestByValue(); -} - -/** \returns an expression of the sub-diagonal vector */ -template -const typename Tridiagonalization::SubDiagonalReturnType -Tridiagonalization::subDiagonal(void) const -{ - int n = m_matrix.rows(); - return Block(m_matrix, 1, 0, n-1,n-1) - .nestByValue().diagonal().nestByValue(); -} - -/** constructs and returns the tridiagonal matrix T. - * Note that the matrix T is equivalent to the diagonal and sub-diagonal of the packed matrix. - * Therefore, it might be often sufficient to directly use the packed matrix, or the vector - * expressions returned by diagonal() and subDiagonal() instead of creating a new matrix. - */ -template -typename Tridiagonalization::MatrixType -Tridiagonalization::matrixT(void) const -{ - // FIXME should this function (and other similar ones) rather take a matrix as argument - // and fill it ? (to avoid temporaries) - int n = m_matrix.rows(); - MatrixType matT = m_matrix; - matT.corner(TopRight,n-1, n-1).diagonal() = subDiagonal().template cast().conjugate(); - if (n>2) - { - matT.corner(TopRight,n-2, n-2).template triangularView().setZero(); - matT.corner(BottomLeft,n-2, n-2).template triangularView().setZero(); - } - return matT; -} - -#ifndef EIGEN_HIDE_HEAVY_CODE - -/** \internal - * Performs a tridiagonal decomposition of \a matA in place. - * - * \param matA the input selfadjoint matrix - * \param hCoeffs returned Householder coefficients - * - * The result is written in the lower triangular part of \a matA. - * - * Implemented from Golub's "Matrix Computations", algorithm 8.3.1. - * - * \sa packedMatrix() - */ -template -void Tridiagonalization::_compute(MatrixType& matA, CoeffVectorType& hCoeffs) -{ - assert(matA.rows()==matA.cols()); - int n = matA.rows(); - Matrix aux(n); - for (int i = 0; i() - * (ei_conj(h) * matA.col(i).end(remainingSize))); - - hCoeffs.end(n-i-1) += (ei_conj(h)*Scalar(-0.5)*(hCoeffs.end(remainingSize).dot(matA.col(i).end(remainingSize)))) * matA.col(i).end(n-i-1); - - matA.corner(BottomRight, remainingSize, remainingSize).template selfadjointView() - .rankUpdate(matA.col(i).end(remainingSize), hCoeffs.end(remainingSize), -1); - - matA.col(i).coeffRef(i+1) = beta; - hCoeffs.coeffRef(i) = h; - } -} - -/** reconstructs and returns the matrix Q */ -template -typename Tridiagonalization::MatrixType -Tridiagonalization::matrixQ(void) const -{ - MatrixType matQ; - matrixQInPlace(&matQ); - return matQ; -} - -template -template -void Tridiagonalization::matrixQInPlace(MatrixBase* q) const -{ - QDerived& matQ = q->derived(); - int n = m_matrix.rows(); - matQ = MatrixType::Identity(n,n); - Matrix aux(n); - for (int i = n-2; i>=0; i--) - { - matQ.corner(BottomRight,n-i-1,n-i-1) - .applyHouseholderOnTheLeft(m_matrix.col(i).end(n-i-2), ei_conj(m_hCoeffs.coeff(i)), &aux.coeffRef(0,0)); - } -} - -/** Performs a full decomposition in place */ -template -void Tridiagonalization::decomposeInPlace(MatrixType& mat, DiagonalType& diag, SubDiagonalType& subdiag, bool extractQ) -{ - int n = mat.rows(); - ei_assert(mat.cols()==n && diag.size()==n && subdiag.size()==n-1); - if (n==3 && (!NumTraits::IsComplex) ) - { - _decomposeInPlace3x3(mat, diag, subdiag, extractQ); - } - else - { - Tridiagonalization tridiag(mat); - diag = tridiag.diagonal(); - subdiag = tridiag.subDiagonal(); - if (extractQ) - tridiag.matrixQInPlace(&mat); - } -} - -/** \internal - * Optimized path for 3x3 matrices. - * Especially useful for plane fitting. - */ -template -void Tridiagonalization::_decomposeInPlace3x3(MatrixType& mat, DiagonalType& diag, SubDiagonalType& subdiag, bool extractQ) -{ - diag[0] = ei_real(mat(0,0)); - RealScalar v1norm2 = ei_abs2(mat(0,2)); - if (ei_isMuchSmallerThan(v1norm2, RealScalar(1))) - { - diag[1] = ei_real(mat(1,1)); - diag[2] = ei_real(mat(2,2)); - subdiag[0] = ei_real(mat(0,1)); - subdiag[1] = ei_real(mat(1,2)); - if (extractQ) - mat.setIdentity(); - } - else - { - RealScalar beta = ei_sqrt(ei_abs2(mat(0,1))+v1norm2); - RealScalar invBeta = RealScalar(1)/beta; - Scalar m01 = mat(0,1) * invBeta; - Scalar m02 = mat(0,2) * invBeta; - Scalar q = RealScalar(2)*m01*mat(1,2) + m02*(mat(2,2) - mat(1,1)); - diag[1] = ei_real(mat(1,1) + m02*q); - diag[2] = ei_real(mat(2,2) - m02*q); - subdiag[0] = beta; - subdiag[1] = ei_real(mat(1,2) - m01 * q); - if (extractQ) - { - mat(0,0) = 1; - mat(0,1) = 0; - mat(0,2) = 0; - mat(1,0) = 0; - mat(1,1) = m01; - mat(1,2) = m02; - mat(2,0) = 0; - mat(2,1) = m02; - mat(2,2) = -m01; - } - } -} - -#endif // EIGEN_HIDE_HEAVY_CODE - -#endif // EIGEN_TRIDIAGONALIZATION_H diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in index dba66c0d9..bd0df9eb2 100644 --- a/doc/Doxyfile.in +++ b/doc/Doxyfile.in @@ -212,6 +212,7 @@ ALIASES = "only_for_vectors=This is only for vectors (either row- "svd_module=This is defined in the %SVD module. \code #include \endcode" \ "geometry_module=This is defined in the %Geometry module. \code #include \endcode" \ "leastsquares_module=This is defined in the %LeastSquares module. \code #include \endcode" \ + "eigensolver_module=This is defined in the %EigenSolver module. \code #include \endcode" \ "label=\bug" \ "redstar=*" \ "nonstableyet=\warning This is not considered to be part of the stable public API yet. Changes may happen in future releases. See \ref Experimental \"Experimental parts of Eigen\"" diff --git a/test/eigensolver_complex.cpp b/test/eigensolver_complex.cpp index dad5a69f8..db129c064 100644 --- a/test/eigensolver_complex.cpp +++ b/test/eigensolver_complex.cpp @@ -23,7 +23,7 @@ // Eigen. If not, see . #include "main.h" -#include +#include #include template void eigensolver(const MatrixType& m) diff --git a/test/eigensolver_generic.cpp b/test/eigensolver_generic.cpp index 2be49faf4..68ee447e9 100644 --- a/test/eigensolver_generic.cpp +++ b/test/eigensolver_generic.cpp @@ -23,7 +23,7 @@ // Eigen. If not, see . #include "main.h" -#include +#include #ifdef HAS_GSL #include "gsl_helper.h" diff --git a/test/eigensolver_selfadjoint.cpp b/test/eigensolver_selfadjoint.cpp index 2571dbf43..61d8f1cb5 100644 --- a/test/eigensolver_selfadjoint.cpp +++ b/test/eigensolver_selfadjoint.cpp @@ -23,7 +23,7 @@ // Eigen. If not, see . #include "main.h" -#include +#include #ifdef HAS_GSL #include "gsl_helper.h" diff --git a/test/product_notemporary.cpp b/test/product_notemporary.cpp index 3b0329f43..1a3d65291 100644 --- a/test/product_notemporary.cpp +++ b/test/product_notemporary.cpp @@ -71,8 +71,6 @@ template void product_notemporary(const MatrixType& m) VERIFY_EVALUATION_COUNT( m3 = (m1 * m2.adjoint()), 1); VERIFY_EVALUATION_COUNT( m3.noalias() = m1 * m2.adjoint(), 0); - // NOTE in this case the slow product is used: - // FIXME: VERIFY_EVALUATION_COUNT( m3.noalias() = s1 * (m1 * m2.transpose()), 0); VERIFY_EVALUATION_COUNT( m3.noalias() = s1 * m1 * s2 * m2.adjoint(), 0); -- cgit v1.2.3 From 00f4b4690872ea333d426611b3942f4aaacc0139 Mon Sep 17 00:00:00 2001 From: Gael Guennebaud Date: Thu, 3 Sep 2009 11:50:06 +0200 Subject: typo in sqrt(complex) --- Eigen/src/EigenSolver/ComplexSchur.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Eigen/src/EigenSolver/ComplexSchur.h b/Eigen/src/EigenSolver/ComplexSchur.h index 915f351fc..1a07fe163 100644 --- a/Eigen/src/EigenSolver/ComplexSchur.h +++ b/Eigen/src/EigenSolver/ComplexSchur.h @@ -88,7 +88,7 @@ std::complex ei_sqrt(const std::complex &z) t = ei_abs(z); - if (ei_abs(ei_real(z)) <= ei_abs(ei_real(z))) + if (ei_abs(ei_real(z)) <= ei_abs(ei_imag(z))) { // No cancellation in these formulas tre = ei_sqrt(0.5*(t + ei_real(z))); -- cgit v1.2.3 From 3eb37fe1fb36be375c6211fc00d56a89b08b12fb Mon Sep 17 00:00:00 2001 From: Gael Guennebaud Date: Thu, 3 Sep 2009 13:03:26 +0200 Subject: update mixingtype unit test to reflect current status, but it is still clear we should allow matrix products between complex and real ? --- Eigen/src/Core/Product.h | 18 +++++- Eigen/src/Core/products/GeneralMatrixMatrix.h | 6 +- test/mixingtypes.cpp | 87 ++++++++++++++++++++++++--- 3 files changed, 97 insertions(+), 14 deletions(-) diff --git a/Eigen/src/Core/Product.h b/Eigen/src/Core/Product.h index 5c6cee426..dfdbca839 100644 --- a/Eigen/src/Core/Product.h +++ b/Eigen/src/Core/Product.h @@ -162,7 +162,11 @@ class GeneralProduct public: EIGEN_PRODUCT_PUBLIC_INTERFACE(GeneralProduct) - GeneralProduct(const Lhs& lhs, const Rhs& rhs) : Base(lhs,rhs) {} + GeneralProduct(const Lhs& lhs, const Rhs& rhs) : Base(lhs,rhs) + { + EIGEN_STATIC_ASSERT((ei_is_same_type::ret), + YOU_MIXED_DIFFERENT_NUMERIC_TYPES__YOU_NEED_TO_USE_THE_CAST_METHOD_OF_MATRIXBASE_TO_CAST_NUMERIC_TYPES_EXPLICITLY) + } EIGEN_STRONG_INLINE Scalar value() const { @@ -197,7 +201,11 @@ class GeneralProduct public: EIGEN_PRODUCT_PUBLIC_INTERFACE(GeneralProduct) - GeneralProduct(const Lhs& lhs, const Rhs& rhs) : Base(lhs,rhs) {} + GeneralProduct(const Lhs& lhs, const Rhs& rhs) : Base(lhs,rhs) + { + EIGEN_STATIC_ASSERT((ei_is_same_type::ret), + YOU_MIXED_DIFFERENT_NUMERIC_TYPES__YOU_NEED_TO_USE_THE_CAST_METHOD_OF_MATRIXBASE_TO_CAST_NUMERIC_TYPES_EXPLICITLY) + } template void scaleAndAddTo(Dest& dest, Scalar alpha) const { @@ -251,7 +259,11 @@ class GeneralProduct public: EIGEN_PRODUCT_PUBLIC_INTERFACE(GeneralProduct) - GeneralProduct(const Lhs& lhs, const Rhs& rhs) : Base(lhs,rhs) {} + GeneralProduct(const Lhs& lhs, const Rhs& rhs) : Base(lhs,rhs) + { + EIGEN_STATIC_ASSERT((ei_is_same_type::ret), + YOU_MIXED_DIFFERENT_NUMERIC_TYPES__YOU_NEED_TO_USE_THE_CAST_METHOD_OF_MATRIXBASE_TO_CAST_NUMERIC_TYPES_EXPLICITLY) + } enum { Side = Lhs::IsVectorAtCompileTime ? OnTheLeft : OnTheRight }; typedef typename ei_meta_if::ret MatrixType; diff --git a/Eigen/src/Core/products/GeneralMatrixMatrix.h b/Eigen/src/Core/products/GeneralMatrixMatrix.h index c4692b872..beec17ee4 100644 --- a/Eigen/src/Core/products/GeneralMatrixMatrix.h +++ b/Eigen/src/Core/products/GeneralMatrixMatrix.h @@ -135,7 +135,11 @@ class GeneralProduct public: EIGEN_PRODUCT_PUBLIC_INTERFACE(GeneralProduct) - GeneralProduct(const Lhs& lhs, const Rhs& rhs) : Base(lhs,rhs) {} + GeneralProduct(const Lhs& lhs, const Rhs& rhs) : Base(lhs,rhs) + { + EIGEN_STATIC_ASSERT((ei_is_same_type::ret), + YOU_MIXED_DIFFERENT_NUMERIC_TYPES__YOU_NEED_TO_USE_THE_CAST_METHOD_OF_MATRIXBASE_TO_CAST_NUMERIC_TYPES_EXPLICITLY) + } template void scaleAndAddTo(Dest& dst, Scalar alpha) const { diff --git a/test/mixingtypes.cpp b/test/mixingtypes.cpp index d14232bd4..52e68aba2 100644 --- a/test/mixingtypes.cpp +++ b/test/mixingtypes.cpp @@ -61,7 +61,75 @@ template void mixingtypes(int size = SizeAtCompileType) VERIFY_RAISES_ASSERT(vf+=vd); VERIFY_RAISES_ASSERT(mcd=md); + vf.dot(vf); + VERIFY_RAISES_ASSERT(vd.dot(vf)); + VERIFY_RAISES_ASSERT(vcf.dot(vf)); // yeah eventually we should allow this but i'm too lazy to make that change now in Dot.h +} // especially as that might be rewritten as cwise product .sum() which would make that automatic. + + +void mixingtypes_large(int size) +{ + static const int SizeAtCompileType = Dynamic; + typedef Matrix Mat_f; + typedef Matrix Mat_d; + typedef Matrix, SizeAtCompileType, SizeAtCompileType> Mat_cf; + typedef Matrix, SizeAtCompileType, SizeAtCompileType> Mat_cd; + typedef Matrix Vec_f; + typedef Matrix Vec_d; + typedef Matrix, SizeAtCompileType, 1> Vec_cf; + typedef Matrix, SizeAtCompileType, 1> Vec_cd; + + Mat_f mf(size,size); + Mat_d md(size,size); + Mat_cf mcf(size,size); + Mat_cd mcd(size,size); + Vec_f vf(size,1); + Vec_d vd(size,1); + Vec_cf vcf(size,1); + Vec_cd vcd(size,1); + mf*mf; + // FIXME large products does not allow mixing types + VERIFY_RAISES_ASSERT(md*mcd); + VERIFY_RAISES_ASSERT(mcd*md); + VERIFY_RAISES_ASSERT(mf*vcf); + VERIFY_RAISES_ASSERT(mcf*vf); + VERIFY_RAISES_ASSERT(mcf *= mf); + // VERIFY_RAISES_ASSERT(vcd = md*vcd); // does not even compile + VERIFY_RAISES_ASSERT(vcf = mcf*vf); + + VERIFY_RAISES_ASSERT(mf*md); + VERIFY_RAISES_ASSERT(mcf*mcd); + VERIFY_RAISES_ASSERT(mcf*vcd); + VERIFY_RAISES_ASSERT(vcf = mf*vf); +} + +template void mixingtypes_small() +{ + int size = SizeAtCompileType; + typedef Matrix Mat_f; + typedef Matrix Mat_d; + typedef Matrix, SizeAtCompileType, SizeAtCompileType> Mat_cf; + typedef Matrix, SizeAtCompileType, SizeAtCompileType> Mat_cd; + typedef Matrix Vec_f; + typedef Matrix Vec_d; + typedef Matrix, SizeAtCompileType, 1> Vec_cf; + typedef Matrix, SizeAtCompileType, 1> Vec_cd; + + Mat_f mf(size,size); + Mat_d md(size,size); + Mat_cf mcf(size,size); + Mat_cd mcd(size,size); + Vec_f vf(size,1); + Vec_d vd(size,1); + Vec_cf vcf(size,1); + Vec_cd vcd(size,1); + + + mf*mf; + // FIXME shall we discard those products ? + // 1) currently they work only if SizeAtCompileType is small enough + // 2) in case we vectorize complexes this might be difficult to still allow that md*mcd; mcd*md; mf*vcf; @@ -69,20 +137,19 @@ template void mixingtypes(int size = SizeAtCompileType) mcf *= mf; vcd = md*vcd; vcf = mcf*vf; - VERIFY_RAISES_ASSERT(mf*md); - VERIFY_RAISES_ASSERT(mcf*mcd); - VERIFY_RAISES_ASSERT(mcf*vcd); +// VERIFY_RAISES_ASSERT(mf*md); // does not even compile +// VERIFY_RAISES_ASSERT(mcf*mcd); // does not even compile +// VERIFY_RAISES_ASSERT(mcf*vcd); // does not even compile VERIFY_RAISES_ASSERT(vcf = mf*vf); - - vf.dot(vf); - VERIFY_RAISES_ASSERT(vd.dot(vf)); - VERIFY_RAISES_ASSERT(vcf.dot(vf)); // yeah eventually we should allow this but i'm too lazy to make that change now in Dot.h -} // especially as that might be rewritten as cwise product .sum() which would make that automatic. +} void test_mixingtypes() { // check that our operator new is indeed called: - CALL_SUBTEST(mixingtypes<3>(3)); - CALL_SUBTEST(mixingtypes<4>(4)); + CALL_SUBTEST(mixingtypes<3>()); + CALL_SUBTEST(mixingtypes<4>()); CALL_SUBTEST(mixingtypes(20)); + + CALL_SUBTEST(mixingtypes_small<4>()); + CALL_SUBTEST(mixingtypes_large(20)); } -- cgit v1.2.3 From 41aea9508e55b83f3834d189ef87118b9066b106 Mon Sep 17 00:00:00 2001 From: Hauke Heibel Date: Thu, 3 Sep 2009 13:46:44 +0200 Subject: This seems to be important for MSVC to optimize the size of empty base classes. --- Eigen/src/Core/MatrixBase.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Eigen/src/Core/MatrixBase.h b/Eigen/src/Core/MatrixBase.h index 9ac964168..fececdd5f 100644 --- a/Eigen/src/Core/MatrixBase.h +++ b/Eigen/src/Core/MatrixBase.h @@ -35,7 +35,9 @@ * * Notice that this class is trivial, it is only used to disambiguate overloaded functions. */ -template struct AnyMatrixBase +template struct AnyMatrixBase + : public ei_special_scalar_op_base::Scalar, + typename NumTraits::Scalar>::Real> { typedef typename ei_plain_matrix_type::type PlainMatrixType; @@ -91,9 +93,7 @@ template struct AnyMatrixBase */ template class MatrixBase #ifndef EIGEN_PARSED_BY_DOXYGEN - : public AnyMatrixBase, - public ei_special_scalar_op_base::Scalar, - typename NumTraits::Scalar>::Real> + : public AnyMatrixBase #endif // not EIGEN_PARSED_BY_DOXYGEN { public: -- cgit v1.2.3 From 82ad37c730264a8acd119f328a7007b93257246c Mon Sep 17 00:00:00 2001 From: Gael Guennebaud Date: Thu, 3 Sep 2009 17:08:38 +0200 Subject: implement the continuous generation algorithm of Givens rotations by Anderson (2000) --- Eigen/src/Jacobi/Jacobi.h | 107 +++++++++++++++++++++++++++++++++------------- 1 file changed, 78 insertions(+), 29 deletions(-) diff --git a/Eigen/src/Jacobi/Jacobi.h b/Eigen/src/Jacobi/Jacobi.h index 3e3cce665..7fd57ed5f 100644 --- a/Eigen/src/Jacobi/Jacobi.h +++ b/Eigen/src/Jacobi/Jacobi.h @@ -87,7 +87,7 @@ template class PlanarRotation }; /** Makes \c *this as a Jacobi rotation \a J such that applying \a J on both the right and left sides of the selfadjoint 2x2 matrix - * \f$ B = \left ( \begin{array}{cc} x & y \\ * & z \end{array} \right )\f$ yields a diagonal matrix \f$ A = J^* B J \f$ + * \f$ B = \left ( \begin{array}{cc} x & y \\ \overline y & z \end{array} \right )\f$ yields a diagonal matrix \f$ A = J^* B J \f$ * * \sa MatrixBase::makeJacobi(const MatrixBase&, int, int), MatrixBase::applyOnTheLeft(), MatrixBase::applyOnTheRight() */ @@ -123,7 +123,7 @@ bool PlanarRotation::makeJacobi(RealScalar x, Scalar y, RealScalar z) } /** Makes \c *this as a Jacobi rotation \c J such that applying \a J on both the right and left sides of the 2x2 selfadjoint matrix - * \f$ B = \left ( \begin{array}{cc} \text{this}_{pp} & \text{this}_{pq} \\ * & \text{this}_{qq} \end{array} \right )\f$ yields + * \f$ B = \left ( \begin{array}{cc} \text{this}_{pp} & \text{this}_{pq} \\ \overline \text{this}_{pq} & \text{this}_{qq} \end{array} \right )\f$ yields * a diagonal matrix \f$ A = J^* B J \f$ * * Example: \include Jacobi_makeJacobi.cpp @@ -140,7 +140,7 @@ inline bool PlanarRotation::makeJacobi(const MatrixBase& m, int /** Makes \c *this as a Givens rotation \c G such that applying \f$ G^* \f$ to the left of the vector * \f$ V = \left ( \begin{array}{c} p \\ q \end{array} \right )\f$ yields: - * \f$ G^* V = \left ( \begin{array}{c} z \\ 0 \end{array} \right )\f$. + * \f$ G^* V = \left ( \begin{array}{c} r \\ 0 \end{array} \right )\f$. * * The value of \a z is returned if \a z is not null (the default is null). * Also note that G is built such that the cosine is always real. @@ -148,6 +148,10 @@ inline bool PlanarRotation::makeJacobi(const MatrixBase& m, int * Example: \include Jacobi_makeGivens.cpp * Output: \verbinclude Jacobi_makeGivens.out * + * This function implements the continuous Givens rotation generation algorithm + * found in Anderson (2000), Discontinuous Plane Rotations and the Symmetric Eigenvalue Problem. + * LAPACK Working Note 150, University of Tennessee, UT-CS-00-454, December 4, 2000. + * * \sa MatrixBase::applyOnTheLeft(), MatrixBase::applyOnTheRight() */ template @@ -159,52 +163,97 @@ void PlanarRotation::makeGivens(const Scalar& p, const Scalar& q, Scalar // specialization for complexes template -void PlanarRotation::makeGivens(const Scalar& p, const Scalar& q, Scalar* z, ei_meta_true) +void PlanarRotation::makeGivens(const Scalar& p, const Scalar& q, Scalar* r, ei_meta_true) { - RealScalar scale, absx, absxy; if(q==Scalar(0)) { - // return identity - m_c = Scalar(1); - m_s = Scalar(0); - if(z) *z = p; + m_c = ei_real(p)<0 ? Scalar(-1) : Scalar(1); + m_s = 0; + if(r) *r = m_c * p; + } + else if(p==Scalar(0)) + { + m_c = 0; + m_s = -q/ei_abs(q); + if(r) *r = ei_abs(q); } else { - scale = ei_norm1(p); - absx = scale * ei_sqrt(ei_abs2(p/scale)); - scale = ei_abs(scale) + ei_norm1(q); - absxy = scale * ei_sqrt((absx/scale)*(absx/scale) + ei_abs2(q/scale)); - m_c = Scalar(absx / absxy); - Scalar np = p/absx; - m_s = -ei_conj(np) * q / absxy; - if(z) *z = np * absxy; + RealScalar p1 = ei_norm1(p); + RealScalar q1 = ei_norm1(q); + if(p1>=q1) + { + Scalar ps = p / p1; + RealScalar p2 = ei_abs2(ps); + Scalar qs = q / p1; + RealScalar q2 = ei_abs2(qs); + + RealScalar u = ei_sqrt(RealScalar(1) + q2/p2); + if(ei_real(p) -void PlanarRotation::makeGivens(const Scalar& p, const Scalar& q, Scalar* z, ei_meta_false) +void PlanarRotation::makeGivens(const Scalar& p, const Scalar& q, Scalar* r, ei_meta_false) { - ei_assert(z==0 && "not implemented yet"); - // from Golub's "Matrix Computations", algorithm 5.1.3 + if(q==0) { - m_c = 1; m_s = 0; + m_c = pei_abs(p)) + else if(p==0) { - Scalar t = -p/q; - m_s = Scalar(1)/ei_sqrt(1+t*t); - m_c = m_s * t; + m_c = 0; + m_s = qq) + { + Scalar t = q/p; + Scalar u = ei_sqrt(Scalar(1) + ei_abs2(t)); + if(p Date: Thu, 3 Sep 2009 17:27:51 +0200 Subject: Added conservativeResize + unit test. --- Eigen/src/Core/Matrix.h | 61 ++++++++++++++++++ Eigen/src/Core/util/StaticAssert.h | 6 ++ test/CMakeLists.txt | 1 + test/conservative_resize.cpp | 129 +++++++++++++++++++++++++++++++++++++ 4 files changed, 197 insertions(+) create mode 100644 test/conservative_resize.cpp diff --git a/Eigen/src/Core/Matrix.h b/Eigen/src/Core/Matrix.h index 2fc38c812..70862ad11 100644 --- a/Eigen/src/Core/Matrix.h +++ b/Eigen/src/Core/Matrix.h @@ -321,6 +321,67 @@ class Matrix else resize(other.rows(), other.cols()); } + /** Resizes \c *this to a \a rows x \a cols matrix while leaving old values of *this untouched. + * + * This method is intended for dynamic-size matrices, although it is legal to call it on any + * matrix as long as fixed dimensions are left unchanged. If you only want to change the number + * of rows and/or of columns, you can use conservativeResize(NoChange_t, int), + * conservativeResize(int, NoChange_t). + * + * The top-left part of the resized matrix will be the same as the overlapping top-left corner + * of *this. In case values need to be appended to the matrix they will be uninitialized per + * default and set to zero when init_with_zero is set to true. + */ + inline void conservativeResize(int rows, int cols, bool init_with_zero = false) + { + // Note: Here is space for improvement. Basically, for conservativeResize(int,int), + // neither RowsAtCompileTime or ColsAtCompileTime must be Dynamic. If only one of the + // dimensions is dynamic, one could use either conservativeResize(int rows, NoChange_t) or + // conservativeResize(NoChange_t, int cols). For these methods new static asserts like + // EIGEN_STATIC_ASSERT_DYNAMIC_ROWS and EIGEN_STATIC_ASSERT_DYNAMIC_COLS would be good. + EIGEN_STATIC_ASSERT_DYNAMIC_SIZE(Matrix) + PlainMatrixType tmp = init_with_zero ? PlainMatrixType::Zero(rows, cols) : PlainMatrixType(rows,cols); + const int common_rows = std::min(rows, this->rows()); + const int common_cols = std::min(cols, this->cols()); + tmp.block(0,0,common_rows,common_cols) = this->block(0,0,common_rows,common_cols); + this->derived().swap(tmp); + } + + EIGEN_STRONG_INLINE void conservativeResize(int rows, NoChange_t, bool init_with_zero = false) + { + // Note: see the comment in conservativeResize(int,int,bool) + conservativeResize(rows, cols(), init_with_zero); + } + + EIGEN_STRONG_INLINE void conservativeResize(NoChange_t, int cols, bool init_with_zero = false) + { + // Note: see the comment in conservativeResize(int,int,bool) + conservativeResize(rows(), cols, init_with_zero); + } + + /** Resizes \c *this to a vector of length \a size while retaining old values of *this. + * + * \only_for_vectors. This method does not work for + * partially dynamic matrices when the static dimension is anything other + * than 1. For example it will not work with Matrix. + * + * When values are appended, they will be uninitialized per default and set + * to zero when init_with_zero is set to true. + */ + inline void conservativeResize(int size, bool init_with_zero = false) + { + EIGEN_STATIC_ASSERT_VECTOR_ONLY(Matrix) + EIGEN_STATIC_ASSERT_DYNAMIC_SIZE(Matrix) + + if (RowsAtCompileTime == 1 || ColsAtCompileTime == 1) + { + PlainMatrixType tmp = init_with_zero ? PlainMatrixType::Zero(size) : PlainMatrixType(size); + const int common_size = std::min(this->size(),size); + tmp.segment(0,common_size) = this->segment(0,common_size); + this->derived().swap(tmp); + } + } + /** Copies the value of the expression \a other into \c *this with automatic resizing. * * *this might be resized to match the dimensions of \a other. If *this was a null matrix (not already initialized), diff --git a/Eigen/src/Core/util/StaticAssert.h b/Eigen/src/Core/util/StaticAssert.h index e1ccd58e7..73d302fda 100644 --- a/Eigen/src/Core/util/StaticAssert.h +++ b/Eigen/src/Core/util/StaticAssert.h @@ -62,6 +62,7 @@ THIS_METHOD_IS_ONLY_FOR_MATRICES_OF_A_SPECIFIC_SIZE, YOU_MADE_A_PROGRAMMING_MISTAKE, YOU_CALLED_A_FIXED_SIZE_METHOD_ON_A_DYNAMIC_SIZE_MATRIX_OR_VECTOR, + YOU_CALLED_A_DYNAMIC_SIZE_METHOD_ON_A_FIXED_SIZE_MATRIX_OR_VECTOR, UNALIGNED_LOAD_AND_STORE_OPERATIONS_UNIMPLEMENTED_ON_ALTIVEC, NUMERIC_TYPE_MUST_BE_FLOATING_POINT, NUMERIC_TYPE_MUST_BE_REAL, @@ -114,6 +115,11 @@ EIGEN_STATIC_ASSERT(TYPE::SizeAtCompileTime!=Eigen::Dynamic, \ YOU_CALLED_A_FIXED_SIZE_METHOD_ON_A_DYNAMIC_SIZE_MATRIX_OR_VECTOR) +// static assertion failing if the type \a TYPE is not dynamic-size +#define EIGEN_STATIC_ASSERT_DYNAMIC_SIZE(TYPE) \ + EIGEN_STATIC_ASSERT(TYPE::SizeAtCompileTime==Eigen::Dynamic, \ + YOU_CALLED_A_DYNAMIC_SIZE_METHOD_ON_A_FIXED_SIZE_MATRIX_OR_VECTOR) + // static assertion failing if the type \a TYPE is not a vector type of the given size #define EIGEN_STATIC_ASSERT_VECTOR_SPECIFIC_SIZE(TYPE, SIZE) \ EIGEN_STATIC_ASSERT(TYPE::IsVectorAtCompileTime && TYPE::SizeAtCompileTime==SIZE, \ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 6bbf82144..03fbd48fc 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -149,6 +149,7 @@ ei_add_test(sparse_solvers " " "${SPARSE_LIBS}") ei_add_test(umeyama) ei_add_test(householder) ei_add_test(swap) +ei_add_test(conservative_resize) ei_add_property(EIGEN_TESTING_SUMMARY "CXX: ${CMAKE_CXX_COMPILER}\n") if(CMAKE_COMPILER_IS_GNUCXX) diff --git a/test/conservative_resize.cpp b/test/conservative_resize.cpp new file mode 100644 index 000000000..f0d025283 --- /dev/null +++ b/test/conservative_resize.cpp @@ -0,0 +1,129 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. Eigen itself is part of the KDE project. +// +// Copyright (C) 2009 Hauke Heibel +// +// 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 or1 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 . + +#include "main.h" + +#include +#include + +using namespace Eigen; + +template +void run_matrix_tests() +{ + typedef Matrix MatrixType; + + MatrixType m, n; + + // boundary cases ... + m = n = MatrixType::Random(50,50); + m.conservativeResize(1,50); + VERIFY_IS_APPROX(m, n.block(0,0,1,50)); + + m = n = MatrixType::Random(50,50); + m.conservativeResize(50,1); + VERIFY_IS_APPROX(m, n.block(0,0,50,1)); + + m = n = MatrixType::Random(50,50); + m.conservativeResize(50,50); + VERIFY_IS_APPROX(m, n.block(0,0,50,50)); + + // random shrinking ... + for (int i=0; i<25; ++i) + { + const int rows = ei_random(1,50); + const int cols = ei_random(1,50); + m = n = MatrixType::Random(50,50); + m.conservativeResize(rows,cols); + VERIFY_IS_APPROX(m, n.block(0,0,rows,cols)); + } + + // random growing with zeroing ... + for (int i=0; i<25; ++i) + { + const int rows = ei_random(50,75); + const int cols = ei_random(50,75); + m = n = MatrixType::Random(50,50); + m.conservativeResize(rows,cols,true); + VERIFY_IS_APPROX(m.block(0,0,n.rows(),n.cols()), n); + VERIFY( rows<=50 || m.block(50,0,rows-50,cols).sum() == Scalar(0) ); + VERIFY( cols<=50 || m.block(0,50,rows,cols-50).sum() == Scalar(0) ); + } +} + +template +void run_vector_tests() +{ + typedef Matrix MatrixType; + + MatrixType m, n; + + // boundary cases ... + m = n = MatrixType::Random(50); + m.conservativeResize(1); + VERIFY_IS_APPROX(m, n.segment(0,1)); + + m = n = MatrixType::Random(50); + m.conservativeResize(50); + VERIFY_IS_APPROX(m, n.segment(0,50)); + + // random shrinking ... + for (int i=0; i<50; ++i) + { + const int size = ei_random(1,50); + m = n = MatrixType::Random(50); + m.conservativeResize(size); + VERIFY_IS_APPROX(m, n.segment(0,size)); + } + + // random growing with zeroing ... + for (int i=0; i<50; ++i) + { + const int size = ei_random(50,100); + m = n = MatrixType::Random(50); + m.conservativeResize(size,true); + VERIFY_IS_APPROX(m.segment(0,50), n); + VERIFY( size<=50 || m.segment(50,size-50).sum() == Scalar(0) ); + } +} + +void test_conservative_resize() +{ + run_matrix_tests(); + run_matrix_tests(); + run_matrix_tests(); + run_matrix_tests(); + run_matrix_tests(); + run_matrix_tests(); + run_matrix_tests, Eigen::RowMajor>(); + run_matrix_tests, Eigen::ColMajor>(); + run_matrix_tests, Eigen::RowMajor>(); + run_matrix_tests, Eigen::ColMajor>(); + + run_vector_tests(); + run_vector_tests(); + run_vector_tests(); + run_vector_tests >(); + run_vector_tests >(); +} -- cgit v1.2.3 From 68b28f7bfb29474ad21036476618a3730fa7fffa Mon Sep 17 00:00:00 2001 From: Gael Guennebaud Date: Fri, 4 Sep 2009 09:23:38 +0200 Subject: rename the EigenSolver module to Eigenvalues --- Eigen/CMakeLists.txt | 2 +- Eigen/EigenSolver | 74 --- Eigen/Eigenvalues | 74 +++ Eigen/LeastSquares | 2 +- Eigen/QR | 4 +- Eigen/src/CMakeLists.txt | 2 +- Eigen/src/EigenSolver/CMakeLists.txt | 6 - Eigen/src/EigenSolver/ComplexEigenSolver.h | 139 ----- Eigen/src/EigenSolver/ComplexSchur.h | 238 -------- Eigen/src/EigenSolver/EigenSolver.h | 723 ------------------------ Eigen/src/EigenSolver/HessenbergDecomposition.h | 206 ------- Eigen/src/EigenSolver/SelfAdjointEigenSolver.h | 366 ------------ Eigen/src/EigenSolver/Tridiagonalization.h | 317 ----------- Eigen/src/Eigenvalues/CMakeLists.txt | 6 + Eigen/src/Eigenvalues/ComplexEigenSolver.h | 148 +++++ Eigen/src/Eigenvalues/ComplexSchur.h | 237 ++++++++ Eigen/src/Eigenvalues/EigenSolver.h | 723 ++++++++++++++++++++++++ Eigen/src/Eigenvalues/HessenbergDecomposition.h | 206 +++++++ Eigen/src/Eigenvalues/SelfAdjointEigenSolver.h | 366 ++++++++++++ Eigen/src/Eigenvalues/Tridiagonalization.h | 317 +++++++++++ doc/Doxyfile.in | 2 +- test/eigensolver_complex.cpp | 2 +- test/eigensolver_generic.cpp | 2 +- test/eigensolver_selfadjoint.cpp | 2 +- test/mixingtypes.cpp | 2 +- 25 files changed, 2087 insertions(+), 2079 deletions(-) delete mode 100644 Eigen/EigenSolver create mode 100644 Eigen/Eigenvalues delete mode 100644 Eigen/src/EigenSolver/CMakeLists.txt delete mode 100644 Eigen/src/EigenSolver/ComplexEigenSolver.h delete mode 100644 Eigen/src/EigenSolver/ComplexSchur.h delete mode 100644 Eigen/src/EigenSolver/EigenSolver.h delete mode 100644 Eigen/src/EigenSolver/HessenbergDecomposition.h delete mode 100644 Eigen/src/EigenSolver/SelfAdjointEigenSolver.h delete mode 100644 Eigen/src/EigenSolver/Tridiagonalization.h create mode 100644 Eigen/src/Eigenvalues/CMakeLists.txt create mode 100644 Eigen/src/Eigenvalues/ComplexEigenSolver.h create mode 100644 Eigen/src/Eigenvalues/ComplexSchur.h create mode 100644 Eigen/src/Eigenvalues/EigenSolver.h create mode 100644 Eigen/src/Eigenvalues/HessenbergDecomposition.h create mode 100644 Eigen/src/Eigenvalues/SelfAdjointEigenSolver.h create mode 100644 Eigen/src/Eigenvalues/Tridiagonalization.h diff --git a/Eigen/CMakeLists.txt b/Eigen/CMakeLists.txt index ebdf57812..931cc6e20 100644 --- a/Eigen/CMakeLists.txt +++ b/Eigen/CMakeLists.txt @@ -1,4 +1,4 @@ -set(Eigen_HEADERS Core LU Cholesky QR Geometry Sparse Array SVD LeastSquares QtAlignedMalloc StdVector Householder Jacobi EigenSolver) +set(Eigen_HEADERS Core LU Cholesky QR Geometry Sparse Array SVD LeastSquares QtAlignedMalloc StdVector Householder Jacobi Eigenvalues) if(EIGEN_BUILD_LIB) set(Eigen_SRCS diff --git a/Eigen/EigenSolver b/Eigen/EigenSolver deleted file mode 100644 index fd126d282..000000000 --- a/Eigen/EigenSolver +++ /dev/null @@ -1,74 +0,0 @@ -#ifndef EIGEN_EIGEN_SOLVER_MODULE_H -#define EIGEN_EIGEN_SOLVER_MODULE_H - -#include "Core" - -#include "src/Core/util/DisableMSVCWarnings.h" - -#include "Cholesky" -#include "Jacobi" -#include "Householder" - -// Note that EIGEN_HIDE_HEAVY_CODE has to be defined per module -#if (defined EIGEN_EXTERN_INSTANTIATIONS) && (EIGEN_EXTERN_INSTANTIATIONS>=2) - #ifndef EIGEN_HIDE_HEAVY_CODE - #define EIGEN_HIDE_HEAVY_CODE - #endif -#elif defined EIGEN_HIDE_HEAVY_CODE - #undef EIGEN_HIDE_HEAVY_CODE -#endif - -namespace Eigen { - -/** \defgroup EigenSolver_Module Eigen Solver module - * - * \nonstableyet - * - * This module mainly provides various eigen value solvers. - * This module also provides some MatrixBase methods, including: - * - MatrixBase::eigenvalues(), - * - MatrixBase::operatorNorm() - * - * \code - * #include - * \endcode - */ - -#include "src/EigenSolver/Tridiagonalization.h" -#include "src/EigenSolver/EigenSolver.h" -#include "src/EigenSolver/SelfAdjointEigenSolver.h" -#include "src/EigenSolver/HessenbergDecomposition.h" -#include "src/EigenSolver/ComplexSchur.h" -#include "src/EigenSolver/ComplexEigenSolver.h" - -// declare all classes for a given matrix type -#define EIGEN_EIGEN_SOLVER_MODULE_INSTANTIATE_TYPE(MATRIXTYPE,PREFIX) \ - PREFIX template class Tridiagonalization; \ - PREFIX template class HessenbergDecomposition; \ - PREFIX template class SelfAdjointEigenSolver - -// removed because it does not support complex yet -// PREFIX template class EigenSolver - -// declare all class for all types -#define EIGEN_EIGEN_SOLVER_MODULE_INSTANTIATE(PREFIX) \ - EIGEN_EIGEN_SOLVER_MODULE_INSTANTIATE_TYPE(Matrix2f,PREFIX); \ - EIGEN_EIGEN_SOLVER_MODULE_INSTANTIATE_TYPE(Matrix2d,PREFIX); \ - EIGEN_EIGEN_SOLVER_MODULE_INSTANTIATE_TYPE(Matrix3f,PREFIX); \ - EIGEN_EIGEN_SOLVER_MODULE_INSTANTIATE_TYPE(Matrix3d,PREFIX); \ - EIGEN_EIGEN_SOLVER_MODULE_INSTANTIATE_TYPE(Matrix4f,PREFIX); \ - EIGEN_EIGEN_SOLVER_MODULE_INSTANTIATE_TYPE(Matrix4d,PREFIX); \ - EIGEN_EIGEN_SOLVER_MODULE_INSTANTIATE_TYPE(MatrixXf,PREFIX); \ - EIGEN_EIGEN_SOLVER_MODULE_INSTANTIATE_TYPE(MatrixXd,PREFIX); \ - EIGEN_EIGEN_SOLVER_MODULE_INSTANTIATE_TYPE(MatrixXcf,PREFIX); \ - EIGEN_EIGEN_SOLVER_MODULE_INSTANTIATE_TYPE(MatrixXcd,PREFIX) - -#ifdef EIGEN_EXTERN_INSTANTIATIONS - EIGEN_EIGEN_SOLVER_MODULE_INSTANTIATE(extern); -#endif // EIGEN_EXTERN_INSTANTIATIONS - -} // namespace Eigen - -#include "src/Core/util/EnableMSVCWarnings.h" - -#endif // EIGEN_EIGEN_SOLVER_MODULE_H diff --git a/Eigen/Eigenvalues b/Eigen/Eigenvalues new file mode 100644 index 000000000..9a6443f39 --- /dev/null +++ b/Eigen/Eigenvalues @@ -0,0 +1,74 @@ +#ifndef EIGEN_EIGENVALUES_MODULE_H +#define EIGEN_EIGENVALUES_MODULE_H + +#include "Core" + +#include "src/Core/util/DisableMSVCWarnings.h" + +#include "Cholesky" +#include "Jacobi" +#include "Householder" + +// Note that EIGEN_HIDE_HEAVY_CODE has to be defined per module +#if (defined EIGEN_EXTERN_INSTANTIATIONS) && (EIGEN_EXTERN_INSTANTIATIONS>=2) + #ifndef EIGEN_HIDE_HEAVY_CODE + #define EIGEN_HIDE_HEAVY_CODE + #endif +#elif defined EIGEN_HIDE_HEAVY_CODE + #undef EIGEN_HIDE_HEAVY_CODE +#endif + +namespace Eigen { + +/** \defgroup Eigenvalues_Module Eigenvalues module + * + * \nonstableyet + * + * This module mainly provides various eigenvalue solvers. + * This module also provides some MatrixBase methods, including: + * - MatrixBase::eigenvalues(), + * - MatrixBase::operatorNorm() + * + * \code + * #include + * \endcode + */ + +#include "src/Eigenvalues/Tridiagonalization.h" +#include "src/Eigenvalues/EigenSolver.h" +#include "src/Eigenvalues/SelfAdjointEigenSolver.h" +#include "src/Eigenvalues/HessenbergDecomposition.h" +#include "src/Eigenvalues/ComplexSchur.h" +#include "src/Eigenvalues/ComplexEigenSolver.h" + +// declare all classes for a given matrix type +#define EIGEN_EIGENVALUES_MODULE_INSTANTIATE_TYPE(MATRIXTYPE,PREFIX) \ + PREFIX template class Tridiagonalization; \ + PREFIX template class HessenbergDecomposition; \ + PREFIX template class SelfAdjointEigenSolver + +// removed because it does not support complex yet +// PREFIX template class EigenSolver + +// declare all class for all types +#define EIGEN_EIGENVALUES_MODULE_INSTANTIATE(PREFIX) \ + EIGEN_EIGENVALUES_MODULE_INSTANTIATE_TYPE(Matrix2f,PREFIX); \ + EIGEN_EIGENVALUES_MODULE_INSTANTIATE_TYPE(Matrix2d,PREFIX); \ + EIGEN_EIGENVALUES_MODULE_INSTANTIATE_TYPE(Matrix3f,PREFIX); \ + EIGEN_EIGENVALUES_MODULE_INSTANTIATE_TYPE(Matrix3d,PREFIX); \ + EIGEN_EIGENVALUES_MODULE_INSTANTIATE_TYPE(Matrix4f,PREFIX); \ + EIGEN_EIGENVALUES_MODULE_INSTANTIATE_TYPE(Matrix4d,PREFIX); \ + EIGEN_EIGENVALUES_MODULE_INSTANTIATE_TYPE(MatrixXf,PREFIX); \ + EIGEN_EIGENVALUES_MODULE_INSTANTIATE_TYPE(MatrixXd,PREFIX); \ + EIGEN_EIGENVALUES_MODULE_INSTANTIATE_TYPE(MatrixXcf,PREFIX); \ + EIGEN_EIGENVALUES_MODULE_INSTANTIATE_TYPE(MatrixXcd,PREFIX) + +#ifdef EIGEN_EXTERN_INSTANTIATIONS + EIGEN_EIGENVALUES_MODULE_INSTANTIATE(extern); +#endif // EIGEN_EXTERN_INSTANTIATIONS + +} // namespace Eigen + +#include "src/Core/util/EnableMSVCWarnings.h" + +#endif // EIGEN_EIGENVALUES_MODULE_H diff --git a/Eigen/LeastSquares b/Eigen/LeastSquares index db620f548..75620a349 100644 --- a/Eigen/LeastSquares +++ b/Eigen/LeastSquares @@ -5,7 +5,7 @@ #include "src/Core/util/DisableMSVCWarnings.h" -#include "EigenSolver" +#include "Eigenvalues" #include "Geometry" namespace Eigen { diff --git a/Eigen/QR b/Eigen/QR index a7273bc8a..f38e96c5e 100644 --- a/Eigen/QR +++ b/Eigen/QR @@ -62,7 +62,7 @@ namespace Eigen { #include "src/Core/util/EnableMSVCWarnings.h" -// FIXME for compatibility we include EigenSolver here: -#include "EigenSolver" +// FIXME for compatibility we include Eigenvalues here: +#include "Eigenvalues" #endif // EIGEN_QR_MODULE_H diff --git a/Eigen/src/CMakeLists.txt b/Eigen/src/CMakeLists.txt index 6b1333860..0df8273d1 100644 --- a/Eigen/src/CMakeLists.txt +++ b/Eigen/src/CMakeLists.txt @@ -9,4 +9,4 @@ ADD_SUBDIRECTORY(LeastSquares) ADD_SUBDIRECTORY(Sparse) ADD_SUBDIRECTORY(Jacobi) ADD_SUBDIRECTORY(Householder) -ADD_SUBDIRECTORY(EigenSolver) +ADD_SUBDIRECTORY(Eigenvalues) diff --git a/Eigen/src/EigenSolver/CMakeLists.txt b/Eigen/src/EigenSolver/CMakeLists.txt deleted file mode 100644 index 63bc75e0c..000000000 --- a/Eigen/src/EigenSolver/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -FILE(GLOB Eigen_EIGENSOLVER_SRCS "*.h") - -INSTALL(FILES - ${Eigen_EIGENSOLVER_SRCS} - DESTINATION ${INCLUDE_INSTALL_DIR}/Eigen/src/EigenSolver COMPONENT Devel - ) diff --git a/Eigen/src/EigenSolver/ComplexEigenSolver.h b/Eigen/src/EigenSolver/ComplexEigenSolver.h deleted file mode 100644 index 2ea7464a6..000000000 --- a/Eigen/src/EigenSolver/ComplexEigenSolver.h +++ /dev/null @@ -1,139 +0,0 @@ -// This file is part of Eigen, a lightweight C++ template library -// for linear algebra. -// -// Copyright (C) 2009 Claire Maurice -// Copyright (C) 2009 Gael Guennebaud -// -// 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 . - -#ifndef EIGEN_COMPLEX_EIGEN_SOLVER_H -#define EIGEN_COMPLEX_EIGEN_SOLVER_H - -#define MAXITER 30 - -template class ComplexEigenSolver -{ - public: - typedef _MatrixType MatrixType; - typedef typename MatrixType::Scalar Scalar; - typedef typename NumTraits::Real RealScalar; - typedef std::complex Complex; - typedef Matrix EigenvalueType; - typedef Matrix EigenvectorType; - - /** - * \brief Default Constructor. - * - * The default constructor is useful in cases in which the user intends to - * perform decompositions via ComplexEigenSolver::compute(const MatrixType&). - */ - ComplexEigenSolver() : m_eivec(), m_eivalues(), m_isInitialized(false) - {} - - ComplexEigenSolver(const MatrixType& matrix) - : m_eivec(matrix.rows(),matrix.cols()), - m_eivalues(matrix.cols()), - m_isInitialized(false) - { - compute(matrix); - } - - EigenvectorType eigenvectors(void) const - { - ei_assert(m_isInitialized && "ComplexEigenSolver is not initialized."); - return m_eivec; - } - - EigenvalueType eigenvalues() const - { - ei_assert(m_isInitialized && "ComplexEigenSolver is not initialized."); - return m_eivalues; - } - - void compute(const MatrixType& matrix); - - protected: - MatrixType m_eivec; - EigenvalueType m_eivalues; - bool m_isInitialized; -}; - - -template -void ComplexEigenSolver::compute(const MatrixType& matrix) -{ - // this code is inspired from Jampack - assert(matrix.cols() == matrix.rows()); - int n = matrix.cols(); - m_eivalues.resize(n,1); - - RealScalar eps = epsilon(); - - // Reduce to complex Schur form - ComplexSchur schur(matrix); - - m_eivalues = schur.matrixT().diagonal(); - - m_eivec.setZero(); - - Scalar d2, z; - RealScalar norm = matrix.norm(); - - // compute the (normalized) eigenvectors - for(int k=n-1 ; k>=0 ; k--) - { - d2 = schur.matrixT().coeff(k,k); - m_eivec.coeffRef(k,k) = Scalar(1.0,0.0); - for(int i=k-1 ; i>=0 ; i--) - { - m_eivec.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; - - } - m_eivec.col(k).normalize(); - } - - m_eivec = schur.matrixU() * m_eivec; - m_isInitialized = true; - - // sort the eigenvalues - { - for (int i=0; i -// -// 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 . - -#ifndef EIGEN_COMPLEX_SCHUR_H -#define EIGEN_COMPLEX_SCHUR_H - -#define MAXITER 30 - -/** \ingroup QR - * - * \class ComplexShur - * - * \brief Performs a complex Shur decomposition of a real or complex square matrix - * - */ -template class ComplexSchur -{ - public: - typedef _MatrixType MatrixType; - typedef typename MatrixType::Scalar Scalar; - typedef typename NumTraits::Real RealScalar; - typedef std::complex Complex; - typedef Matrix ComplexMatrixType; - - /** - * \brief Default Constructor. - * - * The default constructor is useful in cases in which the user intends to - * perform decompositions via ComplexSchur::compute(const MatrixType&). - */ - ComplexSchur() : m_matT(), m_matU(), m_isInitialized(false) - {} - - ComplexSchur(const MatrixType& matrix) - : m_matT(matrix.rows(),matrix.cols()), - m_matU(matrix.rows(),matrix.cols()), - m_isInitialized(false) - { - compute(matrix); - } - - ComplexMatrixType matrixU() const - { - ei_assert(m_isInitialized && "ComplexSchur is not initialized."); - return m_matU; - } - - ComplexMatrixType matrixT() const - { - ei_assert(m_isInitialized && "ComplexShur is not initialized."); - return m_matT; - } - - void compute(const MatrixType& matrix); - - protected: - ComplexMatrixType m_matT, m_matU; - bool m_isInitialized; -}; - -/** Computes the principal value of the square root of the complex \a z. */ -template -std::complex ei_sqrt(const std::complex &z) -{ - RealScalar t, tre, tim; - - t = ei_abs(z); - - if (ei_abs(ei_real(z)) <= ei_abs(ei_imag(z))) - { - // No cancellation in these formulas - tre = ei_sqrt(0.5*(t + ei_real(z))); - tim = ei_sqrt(0.5*(t - ei_real(z))); - } - else - { - // Stable computation of the above formulas - if (z.real() > 0) - { - tre = t + z.real(); - tim = ei_abs(ei_imag(z))*ei_sqrt(0.5/tre); - tre = ei_sqrt(0.5*tre); - } - else - { - tim = t - z.real(); - tre = ei_abs(ei_imag(z))*ei_sqrt(0.5/tim); - tim = ei_sqrt(0.5*tim); - } - } - if(z.imag() < 0) - tim = -tim; - - return (std::complex(tre,tim)); - -} - -template -void ComplexSchur::compute(const MatrixType& matrix) -{ - // this code is inspired from Jampack - assert(matrix.cols() == matrix.rows()); - int n = matrix.cols(); - - // Reduce to Hessenberg form - HessenbergDecomposition hess(matrix); - - m_matT = hess.matrixH(); - m_matU = hess.matrixQ(); - - int iu = m_matT.cols() - 1; - int il; - RealScalar d,sd,sf; - Complex c,b,disc,r1,r2,kappa; - - RealScalar eps = epsilon(); - - int iter = 0; - while(true) - { - //locate the range in which to iterate - while(iu > 0) - { - d = ei_norm1(m_matT.coeffRef(iu,iu)) + ei_norm1(m_matT.coeffRef(iu-1,iu-1)); - sd = ei_norm1(m_matT.coeffRef(iu,iu-1)); - - if(sd >= eps * d) break; // FIXME : precision criterion ?? - - m_matT.coeffRef(iu,iu-1) = Complex(0); - iter = 0; - --iu; - } - if(iu==0) break; - iter++; - - if(iter >= MAXITER) - { - // FIXME : what to do when iter==MAXITER ?? - std::cerr << "MAXITER" << std::endl; - return; - } - - il = iu-1; - while( il > 0 ) - { - // check if the current 2x2 block on the diagonal is upper triangular - d = ei_norm1(m_matT.coeffRef(il,il)) + ei_norm1(m_matT.coeffRef(il-1,il-1)); - sd = ei_norm1(m_matT.coeffRef(il,il-1)); - - if(sd < eps * d) break; // FIXME : precision criterion ?? - - --il; - } - - if( il != 0 ) m_matT.coeffRef(il,il-1) = Complex(0); - - // compute the shift (the normalization by sf is to avoid under/overflow) - Matrix t = m_matT.template block<2,2>(iu-1,iu-1); - sf = t.cwise().abs().sum(); - t /= sf; - - c = t.determinant(); - b = t.diagonal().sum(); - - disc = ei_sqrt(b*b - RealScalar(4)*c); - - 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; - - // perform the QR step using Givens rotations - PlanarRotation rot; - rot.makeGivens(m_matT.coeff(il,il) - kappa, m_matT.coeff(il+1,il)); - - for(int i=il ; i -// -// 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 . - -#ifndef EIGEN_EIGENSOLVER_H -#define EIGEN_EIGENSOLVER_H - -/** \ingroup EigenSolver_Module - * \nonstableyet - * - * \class EigenSolver - * - * \brief Eigen values/vectors solver for non selfadjoint matrices - * - * \param MatrixType the type of the matrix of which we are computing the eigen decomposition - * - * Currently it only support real matrices. - * - * \note this code was adapted from JAMA (public domain) - * - * \sa MatrixBase::eigenvalues(), SelfAdjointEigenSolver - */ -template class EigenSolver -{ - public: - - typedef _MatrixType MatrixType; - typedef typename MatrixType::Scalar Scalar; - typedef typename NumTraits::Real RealScalar; - typedef std::complex Complex; - typedef Matrix EigenvalueType; - typedef Matrix EigenvectorType; - typedef Matrix RealVectorType; - typedef Matrix RealVectorTypeX; - - /** - * \brief Default Constructor. - * - * The default constructor is useful in cases in which the user intends to - * perform decompositions via EigenSolver::compute(const MatrixType&). - */ - EigenSolver() : m_eivec(), m_eivalues(), m_isInitialized(false) {} - - EigenSolver(const MatrixType& matrix) - : m_eivec(matrix.rows(), matrix.cols()), - m_eivalues(matrix.cols()), - m_isInitialized(false) - { - compute(matrix); - } - - - EigenvectorType eigenvectors(void) const; - - /** \returns a real matrix V of pseudo eigenvectors. - * - * 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. - * - * 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$ - * - * \sa pseudoEigenvalueMatrix() - */ - const MatrixType& pseudoEigenvectors() const - { - ei_assert(m_isInitialized && "EigenSolver is not initialized."); - return m_eivec; - } - - MatrixType pseudoEigenvalueMatrix() const; - - /** \returns the eigenvalues as a column vector */ - EigenvalueType eigenvalues() const - { - ei_assert(m_isInitialized && "EigenSolver is not initialized."); - return m_eivalues; - } - - EigenSolver& compute(const MatrixType& matrix); - - private: - - void orthes(MatrixType& matH, RealVectorType& ort); - void hqr2(MatrixType& matH); - - protected: - MatrixType m_eivec; - EigenvalueType m_eivalues; - bool m_isInitialized; -}; - -/** \returns the real block diagonal matrix D of the eigenvalues. - * - * See pseudoEigenvectors() for the details. - */ -template -MatrixType EigenSolver::pseudoEigenvalueMatrix() const -{ - ei_assert(m_isInitialized && "EigenSolver is not initialized."); - int n = m_eivec.cols(); - MatrixType matD = MatrixType::Zero(n,n); - for (int i=0; i(i,i) << ei_real(m_eivalues.coeff(i)), ei_imag(m_eivalues.coeff(i)), - -ei_imag(m_eivalues.coeff(i)), ei_real(m_eivalues.coeff(i)); - ++i; - } - } - return matD; -} - -/** \returns the normalized complex eigenvectors as a matrix of column vectors. - * - * \sa eigenvalues(), pseudoEigenvectors() - */ -template -typename EigenSolver::EigenvectorType EigenSolver::eigenvectors(void) const -{ - ei_assert(m_isInitialized && "EigenSolver is not initialized."); - int n = m_eivec.cols(); - EigenvectorType matV(n,n); - for (int j=0; j(); - } - else - { - // we have a pair of complex eigen values - for (int i=0; i -EigenSolver& EigenSolver::compute(const MatrixType& matrix) -{ - assert(matrix.cols() == matrix.rows()); - int n = matrix.cols(); - m_eivalues.resize(n,1); - - MatrixType matH = matrix; - RealVectorType ort(n); - - // Reduce to Hessenberg form. - orthes(matH, ort); - - // Reduce Hessenberg to real Schur form. - hqr2(matH); - - m_isInitialized = true; - return *this; -} - -// Nonsymmetric reduction to Hessenberg form. -template -void EigenSolver::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).cwise().abs().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 -std::complex cdiv(Scalar xr, Scalar xi, Scalar yr, Scalar yi) -{ - Scalar r,d; - if (ei_abs(yr) > ei_abs(yi)) - { - r = yi/yr; - d = yr + r*yi; - return std::complex((xr + r*xi)/d, (xi - r*xr)/d); - } - else - { - r = yr/yi; - d = yi + r*yr; - return std::complex((r*xr + xi)/d, (r*xi - xr)/d); - } -} - - -// Nonsymmetric reduction from Hessenberg to real Schur form. -template -void EigenSolver::hqr2(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. - - // 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::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().cwise().abs().sum() + matH.corner(BottomLeft,n,n).diagonal().cwise().abs().sum(); - 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)).cwise().abs().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--) - { - p = m_eivalues.coeff(n).real(); - q = m_eivalues.coeff(n).imag(); - - // Scalar vector - if (q == 0) - { - int l = n; - matH.coeffRef(n,n) = 1.0; - for (int i = n-1; i >= 0; i--) - { - w = matH.coeff(i,i) - p; - r = matH.row(i).segment(l,n-l+1).dot(matH.col(n).segment(l, n-l+1)); - - if (m_eivalues.coeff(i).imag() < 0.0) - { - z = w; - s = r; - } - else - { - l = i; - if (m_eivalues.coeff(i).imag() == 0.0) - { - if (w != 0.0) - matH.coeffRef(i,n) = -r / w; - else - matH.coeffRef(i,n) = -r / (eps * norm); - } - else // Solve real equations - { - x = matH.coeff(i,i+1); - y = matH.coeff(i+1,i); - q = (m_eivalues.coeff(i).real() - p) * (m_eivalues.coeff(i).real() - p) + m_eivalues.coeff(i).imag() * m_eivalues.coeff(i).imag(); - t = (x * s - z * r) / q; - matH.coeffRef(i,n) = t; - if (ei_abs(x) > ei_abs(z)) - matH.coeffRef(i+1,n) = (-r - w * t) / x; - else - matH.coeffRef(i+1,n) = (-s - y * t) / z; - } - - // Overflow control - t = ei_abs(matH.coeff(i,n)); - if ((eps * t) * t > 1) - matH.col(n).end(nn-i) /= t; - } - } - } - else if (q < 0) // Complex vector - { - std::complex cc; - int l = n-1; - - // Last vector component imaginary so matrix is triangular - if (ei_abs(matH.coeff(n,n-1)) > ei_abs(matH.coeff(n-1,n))) - { - matH.coeffRef(n-1,n-1) = q / matH.coeff(n,n-1); - matH.coeffRef(n-1,n) = -(matH.coeff(n,n) - p) / matH.coeff(n,n-1); - } - else - { - cc = cdiv(0.0,-matH.coeff(n-1,n),matH.coeff(n-1,n-1)-p,q); - matH.coeffRef(n-1,n-1) = ei_real(cc); - matH.coeffRef(n-1,n) = ei_imag(cc); - } - matH.coeffRef(n,n-1) = 0.0; - matH.coeffRef(n,n) = 1.0; - for (int i = n-2; i >= 0; i--) - { - Scalar ra,sa,vr,vi; - ra = matH.row(i).segment(l, n-l+1).dot(matH.col(n-1).segment(l, n-l+1)); - sa = matH.row(i).segment(l, n-l+1).dot(matH.col(n).segment(l, n-l+1)); - w = matH.coeff(i,i) - p; - - if (m_eivalues.coeff(i).imag() < 0.0) - { - z = w; - r = ra; - s = sa; - } - else - { - l = i; - if (m_eivalues.coeff(i).imag() == 0) - { - cc = cdiv(-ra,-sa,w,q); - matH.coeffRef(i,n-1) = ei_real(cc); - matH.coeffRef(i,n) = ei_imag(cc); - } - else - { - // Solve complex equations - x = matH.coeff(i,i+1); - y = matH.coeff(i+1,i); - vr = (m_eivalues.coeff(i).real() - p) * (m_eivalues.coeff(i).real() - p) + m_eivalues.coeff(i).imag() * m_eivalues.coeff(i).imag() - q * q; - vi = (m_eivalues.coeff(i).real() - p) * Scalar(2) * q; - if ((vr == 0.0) && (vi == 0.0)) - vr = eps * norm * (ei_abs(w) + ei_abs(q) + ei_abs(x) + ei_abs(y) + ei_abs(z)); - - cc= cdiv(x*r-z*ra+q*sa,x*s-z*sa-q*ra,vr,vi); - matH.coeffRef(i,n-1) = ei_real(cc); - matH.coeffRef(i,n) = ei_imag(cc); - if (ei_abs(x) > (ei_abs(z) + ei_abs(q))) - { - matH.coeffRef(i+1,n-1) = (-ra - w * matH.coeff(i,n-1) + q * matH.coeff(i,n)) / x; - matH.coeffRef(i+1,n) = (-sa - w * matH.coeff(i,n) - q * matH.coeff(i,n-1)) / x; - } - else - { - cc = cdiv(-r-y*matH.coeff(i,n-1),-s-y*matH.coeff(i,n),z,q); - matH.coeffRef(i+1,n-1) = ei_real(cc); - matH.coeffRef(i+1,n) = ei_imag(cc); - } - } - - // Overflow control - t = std::max(ei_abs(matH.coeff(i,n-1)),ei_abs(matH.coeff(i,n))); - if ((eps * t) * t > 1) - matH.block(i, n-1, nn-i, 2) /= t; - - } - } - } - } - - // Vectors of isolated roots - for (int i = 0; i < nn; ++i) - { - // FIXME again what's the purpose of this test ? - // in this algo low==0 and high==nn-1 !! - if (i < low || i > high) - { - m_eivec.row(i).end(nn-i) = matH.row(i).end(nn-i); - } - } - - // Back transformation to get eigenvectors of original matrix - int bRows = high-low+1; - for (int j = nn-1; j >= low; j--) - { - int bSize = std::min(j,high)-low+1; - m_eivec.col(j).segment(low, bRows) = (m_eivec.block(low, low, bRows, bSize) * matH.col(j).segment(low, bSize)); - } -} - -#endif // EIGEN_EIGENSOLVER_H diff --git a/Eigen/src/EigenSolver/HessenbergDecomposition.h b/Eigen/src/EigenSolver/HessenbergDecomposition.h deleted file mode 100644 index f782cef30..000000000 --- a/Eigen/src/EigenSolver/HessenbergDecomposition.h +++ /dev/null @@ -1,206 +0,0 @@ -// This file is part of Eigen, a lightweight C++ template library -// for linear algebra. -// -// Copyright (C) 2008 Gael Guennebaud -// -// 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 . - -#ifndef EIGEN_HESSENBERGDECOMPOSITION_H -#define EIGEN_HESSENBERGDECOMPOSITION_H - -/** \ingroup EigenSolver_Module - * \nonstableyet - * - * \class HessenbergDecomposition - * - * \brief Reduces a squared matrix to an Hessemberg form - * - * \param 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. - * - * \sa class Tridiagonalization, class Qr - */ -template class HessenbergDecomposition -{ - public: - - typedef _MatrixType MatrixType; - typedef typename MatrixType::Scalar Scalar; - typedef typename NumTraits::Real RealScalar; - - enum { - Size = MatrixType::RowsAtCompileTime, - SizeMinusOne = MatrixType::RowsAtCompileTime==Dynamic - ? Dynamic - : MatrixType::RowsAtCompileTime-1 - }; - - typedef Matrix CoeffVectorType; - typedef Matrix DiagonalType; - typedef Matrix SubDiagonalType; - - typedef typename NestByValue >::RealReturnType DiagonalReturnType; - - typedef typename NestByValue >,0 > >::RealReturnType SubDiagonalReturnType; - - /** This constructor initializes a HessenbergDecomposition object for - * further use with HessenbergDecomposition::compute() - */ - HessenbergDecomposition(int size = Size==Dynamic ? 2 : Size) - : m_matrix(size,size), m_hCoeffs(size-1) - {} - - HessenbergDecomposition(const MatrixType& matrix) - : m_matrix(matrix), - m_hCoeffs(matrix.cols()-1) - { - _compute(m_matrix, m_hCoeffs); - } - - /** Computes or re-compute the Hessenberg decomposition for the matrix \a matrix. - * - * This method allows to re-use the allocated data. - */ - void compute(const MatrixType& matrix) - { - m_matrix = matrix; - m_hCoeffs.resize(matrix.rows()-1,1); - _compute(m_matrix, m_hCoeffs); - } - - /** \returns the householder coefficients allowing to - * reconstruct the matrix Q from the packed data. - * - * \sa packedMatrix() - */ - CoeffVectorType householderCoefficients() const { return m_hCoeffs; } - - /** \returns the internal result of the decomposition. - * - * 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) ] - * - * See LAPACK for further details on this packed storage. - */ - const MatrixType& packedMatrix(void) const { return m_matrix; } - - MatrixType matrixQ() const; - MatrixType matrixH() const; - - private: - - static void _compute(MatrixType& matA, CoeffVectorType& hCoeffs); - - protected: - MatrixType m_matrix; - CoeffVectorType m_hCoeffs; -}; - -#ifndef EIGEN_HIDE_HEAVY_CODE - -/** \internal - * Performs a tridiagonal decomposition of \a matA in place. - * - * \param matA the input selfadjoint matrix - * \param hCoeffs returned Householder coefficients - * - * The result is written in the lower triangular part of \a matA. - * - * Implemented from Golub's "Matrix Computations", algorithm 8.3.1. - * - * \sa packedMatrix() - */ -template -void HessenbergDecomposition::_compute(MatrixType& matA, CoeffVectorType& hCoeffs) -{ - assert(matA.rows()==matA.cols()); - int n = matA.rows(); - Matrix temp(n); - for (int i = 0; i -typename HessenbergDecomposition::MatrixType -HessenbergDecomposition::matrixQ() const -{ - int n = m_matrix.rows(); - MatrixType matQ = MatrixType::Identity(n,n); - Matrix temp(n); - for (int i = n-2; i>=0; i--) - { - matQ.corner(BottomRight,n-i-1,n-i-1) - .applyHouseholderOnTheLeft(m_matrix.col(i).end(n-i-2), ei_conj(m_hCoeffs.coeff(i)), &temp.coeffRef(0,0)); - } - return matQ; -} - -#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 HessenbergDecomposition::MatrixType -HessenbergDecomposition::matrixH() const -{ - // FIXME should this function (and other similar) rather take a matrix as argument - // and fill it (to avoid temporaries) - int n = m_matrix.rows(); - MatrixType matH = m_matrix; - if (n>2) - matH.corner(BottomLeft,n-2, n-2).template triangularView().setZero(); - return matH; -} - -#endif // EIGEN_HESSENBERGDECOMPOSITION_H diff --git a/Eigen/src/EigenSolver/SelfAdjointEigenSolver.h b/Eigen/src/EigenSolver/SelfAdjointEigenSolver.h deleted file mode 100644 index 40b06df2c..000000000 --- a/Eigen/src/EigenSolver/SelfAdjointEigenSolver.h +++ /dev/null @@ -1,366 +0,0 @@ -// This file is part of Eigen, a lightweight C++ template library -// for linear algebra. -// -// Copyright (C) 2008 Gael Guennebaud -// -// 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 . - -#ifndef EIGEN_SELFADJOINTEIGENSOLVER_H -#define EIGEN_SELFADJOINTEIGENSOLVER_H - -/** \eigensolver_module \ingroup EigenSolver_Module - * \nonstableyet - * - * \class SelfAdjointEigenSolver - * - * \brief Eigen values/vectors solver for selfadjoint matrix - * - * \param MatrixType the type of the matrix of which we are computing the eigen decomposition - * - * \note MatrixType must be an actual Matrix type, it can't be an expression type. - * - * \sa MatrixBase::eigenvalues(), class EigenSolver - */ -template class SelfAdjointEigenSolver -{ - public: - - enum {Size = _MatrixType::RowsAtCompileTime }; - typedef _MatrixType MatrixType; - typedef typename MatrixType::Scalar Scalar; - typedef typename NumTraits::Real RealScalar; - typedef std::complex Complex; - typedef Matrix RealVectorType; - typedef Matrix RealVectorTypeX; - typedef Tridiagonalization TridiagonalizationType; -// typedef typename TridiagonalizationType::TridiagonalMatrixType TridiagonalMatrixType; - - SelfAdjointEigenSolver() - : m_eivec(int(Size), int(Size)), - m_eivalues(int(Size)) - { - ei_assert(Size!=Dynamic); - } - - SelfAdjointEigenSolver(int size) - : m_eivec(size, size), - m_eivalues(size) - {} - - /** Constructors computing the eigenvalues of the selfadjoint matrix \a matrix, - * as well as the eigenvectors if \a computeEigenvectors is true. - * - * \sa compute(MatrixType,bool), SelfAdjointEigenSolver(MatrixType,MatrixType,bool) - */ - SelfAdjointEigenSolver(const MatrixType& matrix, bool computeEigenvectors = true) - : m_eivec(matrix.rows(), matrix.cols()), - m_eivalues(matrix.cols()) - { - compute(matrix, computeEigenvectors); - } - - /** Constructors computing the eigenvalues of the generalized eigen problem - * \f$ Ax = lambda B x \f$ with \a matA the selfadjoint matrix \f$ A \f$ - * and \a matB the positive definite matrix \f$ B \f$ . The eigenvectors - * are computed if \a computeEigenvectors is true. - * - * \sa compute(MatrixType,MatrixType,bool), SelfAdjointEigenSolver(MatrixType,bool) - */ - SelfAdjointEigenSolver(const MatrixType& matA, const MatrixType& matB, bool computeEigenvectors = true) - : m_eivec(matA.rows(), matA.cols()), - m_eivalues(matA.cols()) - { - compute(matA, matB, computeEigenvectors); - } - - SelfAdjointEigenSolver& compute(const MatrixType& matrix, bool computeEigenvectors = true); - - SelfAdjointEigenSolver& compute(const MatrixType& matA, const MatrixType& matB, bool computeEigenvectors = true); - - /** \returns the computed eigen vectors as a matrix of column vectors */ - MatrixType eigenvectors(void) const - { - #ifndef NDEBUG - ei_assert(m_eigenvectorsOk); - #endif - return m_eivec; - } - - /** \returns the computed eigen values */ - RealVectorType eigenvalues(void) const { return m_eivalues; } - - /** \returns the positive square root of the matrix - * - * \note the matrix itself must be positive in order for this to make sense. - */ - MatrixType operatorSqrt() const - { - return m_eivec * m_eivalues.cwise().sqrt().asDiagonal() * m_eivec.adjoint(); - } - - /** \returns the positive inverse square root of the matrix - * - * \note the matrix itself must be positive definite in order for this to make sense. - */ - MatrixType operatorInverseSqrt() const - { - return m_eivec * m_eivalues.cwise().inverse().cwise().sqrt().asDiagonal() * m_eivec.adjoint(); - } - - - protected: - MatrixType m_eivec; - RealVectorType m_eivalues; - #ifndef NDEBUG - bool m_eigenvectorsOk; - #endif -}; - -#ifndef EIGEN_HIDE_HEAVY_CODE - -/** \internal - * - * \eigensolver_module - * - * Performs a QR step on a tridiagonal symmetric matrix represented as a - * pair of two vectors \a diag and \a subdiag. - * - * \param matA the input selfadjoint matrix - * \param hCoeffs returned Householder coefficients - * - * For compilation efficiency reasons, this procedure does not use eigen expression - * for its arguments. - * - * Implemented from Golub's "Matrix Computations", algorithm 8.3.2: - * "implicit symmetric QR step with Wilkinson shift" - */ -template -static void ei_tridiagonal_qr_step(RealScalar* diag, RealScalar* subdiag, int start, int end, Scalar* matrixQ, int n); - -/** Computes the eigenvalues of the selfadjoint matrix \a matrix, - * as well as the eigenvectors if \a computeEigenvectors is true. - * - * \sa SelfAdjointEigenSolver(MatrixType,bool), compute(MatrixType,MatrixType,bool) - */ -template -SelfAdjointEigenSolver& SelfAdjointEigenSolver::compute(const MatrixType& matrix, bool computeEigenvectors) -{ - #ifndef NDEBUG - m_eigenvectorsOk = computeEigenvectors; - #endif - assert(matrix.cols() == matrix.rows()); - int n = matrix.cols(); - m_eivalues.resize(n,1); - - if(n==1) - { - m_eivalues.coeffRef(0,0) = ei_real(matrix.coeff(0,0)); - m_eivec.setOnes(); - return *this; - } - - m_eivec = matrix; - - // FIXME, should tridiag be a local variable of this function or an attribute of SelfAdjointEigenSolver ? - // the latter avoids multiple memory allocation when the same SelfAdjointEigenSolver is used multiple times... - // (same for diag and subdiag) - RealVectorType& diag = m_eivalues; - typename TridiagonalizationType::SubDiagonalType subdiag(n-1); - TridiagonalizationType::decomposeInPlace(m_eivec, diag, subdiag, computeEigenvectors); - - int end = n-1; - int start = 0; - while (end>0) - { - for (int i = start; i0 && subdiag[end-1]==0) - end--; - if (end<=0) - break; - start = end - 1; - while (start>0 && subdiag[start-1]!=0) - start--; - - ei_tridiagonal_qr_step(diag.data(), subdiag.data(), start, end, computeEigenvectors ? m_eivec.data() : (Scalar*)0, n); - } - - // Sort eigenvalues and corresponding vectors. - // TODO make the sort optional ? - // TODO use a better sort algorithm !! - for (int i = 0; i < n-1; ++i) - { - int k; - m_eivalues.segment(i,n-i).minCoeff(&k); - if (k > 0) - { - std::swap(m_eivalues[i], m_eivalues[k+i]); - m_eivec.col(i).swap(m_eivec.col(k+i)); - } - } - return *this; -} - -/** Computes the eigenvalues of the generalized eigen problem - * \f$ Ax = lambda B x \f$ with \a matA the selfadjoint matrix \f$ A \f$ - * and \a matB the positive definite matrix \f$ B \f$ . The eigenvectors - * are computed if \a computeEigenvectors is true. - * - * \sa SelfAdjointEigenSolver(MatrixType,MatrixType,bool), compute(MatrixType,bool) - */ -template -SelfAdjointEigenSolver& SelfAdjointEigenSolver:: -compute(const MatrixType& matA, const MatrixType& matB, bool computeEigenvectors) -{ - ei_assert(matA.cols()==matA.rows() && matB.rows()==matA.rows() && matB.cols()==matB.rows()); - - // Compute the cholesky decomposition of matB = L L' - LLT cholB(matB); - - // compute C = inv(L) A inv(L') - MatrixType matC = matA; - cholB.matrixL().solveInPlace(matC); - // FIXME since we currently do not support A * inv(L'), let's do (inv(L) A')' : - matC.adjointInPlace(); - cholB.matrixL().solveInPlace(matC); - matC.adjointInPlace(); - // this version works too: -// matC = matC.transpose(); -// cholB.matrixL().conjugate().template marked().solveTriangularInPlace(matC); -// matC = matC.transpose(); - // FIXME: this should work: (currently it only does for small matrices) -// Transpose trMatC(matC); -// cholB.matrixL().conjugate().eval().template marked().solveTriangularInPlace(trMatC); - - compute(matC, computeEigenvectors); - - if (computeEigenvectors) - { - // transform back the eigen vectors: evecs = inv(U) * evecs - cholB.matrixU().solveInPlace(m_eivec); - for (int i=0; i -inline Matrix::Scalar>::Real, ei_traits::ColsAtCompileTime, 1> -MatrixBase::eigenvalues() const -{ - ei_assert(Flags&SelfAdjointBit); - return SelfAdjointEigenSolver(eval(),false).eigenvalues(); -} - -template -struct ei_operatorNorm_selector -{ - static inline typename NumTraits::Scalar>::Real - operatorNorm(const MatrixBase& m) - { - // FIXME if it is really guaranteed that the eigenvalues are already sorted, - // then we don't need to compute a maxCoeff() here, comparing the 1st and last ones is enough. - return m.eigenvalues().cwise().abs().maxCoeff(); - } -}; - -template struct ei_operatorNorm_selector -{ - static inline typename NumTraits::Scalar>::Real - operatorNorm(const MatrixBase& m) - { - typename Derived::PlainMatrixType m_eval(m); - // FIXME if it is really guaranteed that the eigenvalues are already sorted, - // then we don't need to compute a maxCoeff() here, comparing the 1st and last ones is enough. - return ei_sqrt( - (m_eval*m_eval.adjoint()) - .template marked() - .eigenvalues() - .maxCoeff() - ); - } -}; - -/** \eigensolver_module - * - * \returns the matrix norm of this matrix. - */ -template -inline typename NumTraits::Scalar>::Real -MatrixBase::operatorNorm() const -{ - return ei_operatorNorm_selector - ::operatorNorm(derived()); -} - -#ifndef EIGEN_EXTERN_INSTANTIATIONS -template -static void ei_tridiagonal_qr_step(RealScalar* diag, RealScalar* subdiag, int start, int end, Scalar* matrixQ, int n) -{ - RealScalar td = (diag[end-1] - diag[end])*RealScalar(0.5); - RealScalar e2 = ei_abs2(subdiag[end-1]); - RealScalar mu = diag[end] - e2 / (td + (td>0 ? 1 : -1) * ei_sqrt(td*td + e2)); - RealScalar x = diag[start] - mu; - RealScalar z = subdiag[start]; - - for (int k = start; k < end; ++k) - { - PlanarRotation rot; - rot.makeGivens(x, z); - - // do T = G' T G - RealScalar sdk = rot.s() * diag[k] + rot.c() * subdiag[k]; - RealScalar dkp1 = rot.s() * subdiag[k] + rot.c() * diag[k+1]; - - diag[k] = rot.c() * (rot.c() * diag[k] - rot.s() * subdiag[k]) - rot.s() * (rot.c() * subdiag[k] - rot.s() * diag[k+1]); - diag[k+1] = rot.s() * sdk + rot.c() * dkp1; - subdiag[k] = rot.c() * sdk - rot.s() * dkp1; - - if (k > start) - subdiag[k - 1] = rot.c() * subdiag[k-1] - rot.s() * z; - - x = subdiag[k]; - - if (k < end - 1) - { - z = -rot.s() * subdiag[k+1]; - subdiag[k + 1] = rot.c() * subdiag[k+1]; - } - - // apply the givens rotation to the unit matrix Q = Q * G - if (matrixQ) - { - Map > q(matrixQ,n,n); - q.applyOnTheRight(k,k+1,rot); - } - } -} -#endif - -#endif // EIGEN_SELFADJOINTEIGENSOLVER_H diff --git a/Eigen/src/EigenSolver/Tridiagonalization.h b/Eigen/src/EigenSolver/Tridiagonalization.h deleted file mode 100644 index e0bff17b9..000000000 --- a/Eigen/src/EigenSolver/Tridiagonalization.h +++ /dev/null @@ -1,317 +0,0 @@ -// This file is part of Eigen, a lightweight C++ template library -// for linear algebra. -// -// Copyright (C) 2008 Gael Guennebaud -// -// 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 . - -#ifndef EIGEN_TRIDIAGONALIZATION_H -#define EIGEN_TRIDIAGONALIZATION_H - -/** \ingroup EigenSolver_Module - * \nonstableyet - * - * \class Tridiagonalization - * - * \brief Trigiagonal decomposition of a selfadjoint matrix - * - * \param MatrixType the type of the matrix of which we are performing the tridiagonalization - * - * This class performs a tridiagonal decomposition of a selfadjoint matrix \f$ A \f$ such that: - * \f$ A = Q T Q^* \f$ where \f$ Q \f$ is unitary and \f$ T \f$ a real symmetric tridiagonal matrix. - * - * \sa MatrixBase::tridiagonalize() - */ -template class Tridiagonalization -{ - public: - - typedef _MatrixType MatrixType; - typedef typename MatrixType::Scalar Scalar; - typedef typename NumTraits::Real RealScalar; - typedef typename ei_packet_traits::type Packet; - - enum { - Size = MatrixType::RowsAtCompileTime, - SizeMinusOne = MatrixType::RowsAtCompileTime==Dynamic - ? Dynamic - : MatrixType::RowsAtCompileTime-1, - PacketSize = ei_packet_traits::size - }; - - typedef Matrix CoeffVectorType; - typedef Matrix DiagonalType; - typedef Matrix SubDiagonalType; - - typedef typename ei_meta_if::IsComplex, - typename NestByValue >::RealReturnType, - Diagonal - >::ret DiagonalReturnType; - - typedef typename ei_meta_if::IsComplex, - typename NestByValue >,0 > >::RealReturnType, - Diagonal< - NestByValue >,0 > - >::ret SubDiagonalReturnType; - - /** This constructor initializes a Tridiagonalization object for - * further use with Tridiagonalization::compute() - */ - Tridiagonalization(int size = Size==Dynamic ? 2 : Size) - : m_matrix(size,size), m_hCoeffs(size-1) - {} - - Tridiagonalization(const MatrixType& matrix) - : m_matrix(matrix), m_hCoeffs(matrix.cols()-1) - { - _compute(m_matrix, m_hCoeffs); - } - - /** Computes or re-compute the tridiagonalization for the matrix \a matrix. - * - * This method allows to re-use the allocated data. - */ - void compute(const MatrixType& matrix) - { - m_matrix = matrix; - m_hCoeffs.resize(matrix.rows()-1, 1); - _compute(m_matrix, m_hCoeffs); - } - - /** \returns the householder coefficients allowing to - * reconstruct the matrix Q from the packed data. - * - * \sa packedMatrix() - */ - inline CoeffVectorType householderCoefficients(void) const { return m_hCoeffs; } - - /** \returns the internal result of the decomposition. - * - * The returned matrix contains the following information: - * - the strict upper part is equal to the input matrix A - * - the diagonal and lower sub-diagonal represent the tridiagonal symmetric matrix (real). - * - 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 transformations: - * 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) ] - * - * See LAPACK for further details on this packed storage. - */ - inline const MatrixType& packedMatrix(void) const { return m_matrix; } - - MatrixType matrixQ() const; - template void matrixQInPlace(MatrixBase* q) const; - MatrixType matrixT() const; - const DiagonalReturnType diagonal(void) const; - const SubDiagonalReturnType subDiagonal(void) const; - - static void decomposeInPlace(MatrixType& mat, DiagonalType& diag, SubDiagonalType& subdiag, bool extractQ = true); - - static void _compute(MatrixType& matA, CoeffVectorType& hCoeffs); - - protected: - - static void _decomposeInPlace3x3(MatrixType& mat, DiagonalType& diag, SubDiagonalType& subdiag, bool extractQ = true); - - MatrixType m_matrix; - CoeffVectorType m_hCoeffs; -}; - -/** \returns an expression of the diagonal vector */ -template -const typename Tridiagonalization::DiagonalReturnType -Tridiagonalization::diagonal(void) const -{ - return m_matrix.diagonal().nestByValue(); -} - -/** \returns an expression of the sub-diagonal vector */ -template -const typename Tridiagonalization::SubDiagonalReturnType -Tridiagonalization::subDiagonal(void) const -{ - int n = m_matrix.rows(); - return Block(m_matrix, 1, 0, n-1,n-1) - .nestByValue().diagonal().nestByValue(); -} - -/** constructs and returns the tridiagonal matrix T. - * Note that the matrix T is equivalent to the diagonal and sub-diagonal of the packed matrix. - * Therefore, it might be often sufficient to directly use the packed matrix, or the vector - * expressions returned by diagonal() and subDiagonal() instead of creating a new matrix. - */ -template -typename Tridiagonalization::MatrixType -Tridiagonalization::matrixT(void) const -{ - // FIXME should this function (and other similar ones) rather take a matrix as argument - // and fill it ? (to avoid temporaries) - int n = m_matrix.rows(); - MatrixType matT = m_matrix; - matT.corner(TopRight,n-1, n-1).diagonal() = subDiagonal().template cast().conjugate(); - if (n>2) - { - matT.corner(TopRight,n-2, n-2).template triangularView().setZero(); - matT.corner(BottomLeft,n-2, n-2).template triangularView().setZero(); - } - return matT; -} - -#ifndef EIGEN_HIDE_HEAVY_CODE - -/** \internal - * Performs a tridiagonal decomposition of \a matA in place. - * - * \param matA the input selfadjoint matrix - * \param hCoeffs returned Householder coefficients - * - * The result is written in the lower triangular part of \a matA. - * - * Implemented from Golub's "Matrix Computations", algorithm 8.3.1. - * - * \sa packedMatrix() - */ -template -void Tridiagonalization::_compute(MatrixType& matA, CoeffVectorType& hCoeffs) -{ - assert(matA.rows()==matA.cols()); - int n = matA.rows(); - Matrix aux(n); - for (int i = 0; i() - * (ei_conj(h) * matA.col(i).end(remainingSize))); - - hCoeffs.end(n-i-1) += (ei_conj(h)*Scalar(-0.5)*(hCoeffs.end(remainingSize).dot(matA.col(i).end(remainingSize)))) * matA.col(i).end(n-i-1); - - matA.corner(BottomRight, remainingSize, remainingSize).template selfadjointView() - .rankUpdate(matA.col(i).end(remainingSize), hCoeffs.end(remainingSize), -1); - - matA.col(i).coeffRef(i+1) = beta; - hCoeffs.coeffRef(i) = h; - } -} - -/** reconstructs and returns the matrix Q */ -template -typename Tridiagonalization::MatrixType -Tridiagonalization::matrixQ(void) const -{ - MatrixType matQ; - matrixQInPlace(&matQ); - return matQ; -} - -template -template -void Tridiagonalization::matrixQInPlace(MatrixBase* q) const -{ - QDerived& matQ = q->derived(); - int n = m_matrix.rows(); - matQ = MatrixType::Identity(n,n); - Matrix aux(n); - for (int i = n-2; i>=0; i--) - { - matQ.corner(BottomRight,n-i-1,n-i-1) - .applyHouseholderOnTheLeft(m_matrix.col(i).end(n-i-2), ei_conj(m_hCoeffs.coeff(i)), &aux.coeffRef(0,0)); - } -} - -/** Performs a full decomposition in place */ -template -void Tridiagonalization::decomposeInPlace(MatrixType& mat, DiagonalType& diag, SubDiagonalType& subdiag, bool extractQ) -{ - int n = mat.rows(); - ei_assert(mat.cols()==n && diag.size()==n && subdiag.size()==n-1); - if (n==3 && (!NumTraits::IsComplex) ) - { - _decomposeInPlace3x3(mat, diag, subdiag, extractQ); - } - else - { - Tridiagonalization tridiag(mat); - diag = tridiag.diagonal(); - subdiag = tridiag.subDiagonal(); - if (extractQ) - tridiag.matrixQInPlace(&mat); - } -} - -/** \internal - * Optimized path for 3x3 matrices. - * Especially useful for plane fitting. - */ -template -void Tridiagonalization::_decomposeInPlace3x3(MatrixType& mat, DiagonalType& diag, SubDiagonalType& subdiag, bool extractQ) -{ - diag[0] = ei_real(mat(0,0)); - RealScalar v1norm2 = ei_abs2(mat(0,2)); - if (ei_isMuchSmallerThan(v1norm2, RealScalar(1))) - { - diag[1] = ei_real(mat(1,1)); - diag[2] = ei_real(mat(2,2)); - subdiag[0] = ei_real(mat(0,1)); - subdiag[1] = ei_real(mat(1,2)); - if (extractQ) - mat.setIdentity(); - } - else - { - RealScalar beta = ei_sqrt(ei_abs2(mat(0,1))+v1norm2); - RealScalar invBeta = RealScalar(1)/beta; - Scalar m01 = mat(0,1) * invBeta; - Scalar m02 = mat(0,2) * invBeta; - Scalar q = RealScalar(2)*m01*mat(1,2) + m02*(mat(2,2) - mat(1,1)); - diag[1] = ei_real(mat(1,1) + m02*q); - diag[2] = ei_real(mat(2,2) - m02*q); - subdiag[0] = beta; - subdiag[1] = ei_real(mat(1,2) - m01 * q); - if (extractQ) - { - mat(0,0) = 1; - mat(0,1) = 0; - mat(0,2) = 0; - mat(1,0) = 0; - mat(1,1) = m01; - mat(1,2) = m02; - mat(2,0) = 0; - mat(2,1) = m02; - mat(2,2) = -m01; - } - } -} - -#endif // EIGEN_HIDE_HEAVY_CODE - -#endif // EIGEN_TRIDIAGONALIZATION_H diff --git a/Eigen/src/Eigenvalues/CMakeLists.txt b/Eigen/src/Eigenvalues/CMakeLists.txt new file mode 100644 index 000000000..193e02685 --- /dev/null +++ b/Eigen/src/Eigenvalues/CMakeLists.txt @@ -0,0 +1,6 @@ +FILE(GLOB Eigen_EIGENVALUES_SRCS "*.h") + +INSTALL(FILES + ${Eigen_EIGENVALUES_SRCS} + DESTINATION ${INCLUDE_INSTALL_DIR}/Eigen/src/Eigenvalues COMPONENT Devel + ) diff --git a/Eigen/src/Eigenvalues/ComplexEigenSolver.h b/Eigen/src/Eigenvalues/ComplexEigenSolver.h new file mode 100644 index 000000000..666381949 --- /dev/null +++ b/Eigen/src/Eigenvalues/ComplexEigenSolver.h @@ -0,0 +1,148 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2009 Claire Maurice +// Copyright (C) 2009 Gael Guennebaud +// +// 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 . + +#ifndef EIGEN_COMPLEX_EIGEN_SOLVER_H +#define EIGEN_COMPLEX_EIGEN_SOLVER_H + +/** \eigenvalues_module \ingroup Eigenvalues_Module + * \nonstableyet + * + * \class ComplexEigenSolver + * + * \brief Eigen values/vectors solver for general complex matrices + * + * \param MatrixType the type of the matrix of which we are computing the eigen decomposition + * + * \sa class EigenSolver, class SelfAdjointEigenSolver + */ +template class ComplexEigenSolver +{ + public: + typedef _MatrixType MatrixType; + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits::Real RealScalar; + typedef std::complex Complex; + typedef Matrix EigenvalueType; + typedef Matrix EigenvectorType; + + /** + * \brief Default Constructor. + * + * The default constructor is useful in cases in which the user intends to + * perform decompositions via ComplexEigenSolver::compute(const MatrixType&). + */ + ComplexEigenSolver() : m_eivec(), m_eivalues(), m_isInitialized(false) + {} + + ComplexEigenSolver(const MatrixType& matrix) + : m_eivec(matrix.rows(),matrix.cols()), + m_eivalues(matrix.cols()), + m_isInitialized(false) + { + compute(matrix); + } + + EigenvectorType eigenvectors(void) const + { + ei_assert(m_isInitialized && "ComplexEigenSolver is not initialized."); + return m_eivec; + } + + EigenvalueType eigenvalues() const + { + ei_assert(m_isInitialized && "ComplexEigenSolver is not initialized."); + return m_eivalues; + } + + void compute(const MatrixType& matrix); + + protected: + MatrixType m_eivec; + EigenvalueType m_eivalues; + bool m_isInitialized; +}; + + +template +void ComplexEigenSolver::compute(const MatrixType& matrix) +{ + // this code is inspired from Jampack + assert(matrix.cols() == matrix.rows()); + int n = matrix.cols(); + m_eivalues.resize(n,1); + + RealScalar eps = epsilon(); + + // Reduce to complex Schur form + ComplexSchur schur(matrix); + + m_eivalues = schur.matrixT().diagonal(); + + m_eivec.setZero(); + + Scalar d2, z; + RealScalar norm = matrix.norm(); + + // compute the (normalized) eigenvectors + for(int k=n-1 ; k>=0 ; k--) + { + d2 = schur.matrixT().coeff(k,k); + m_eivec.coeffRef(k,k) = Scalar(1.0,0.0); + for(int i=k-1 ; i>=0 ; i--) + { + m_eivec.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; + + } + m_eivec.col(k).normalize(); + } + + m_eivec = schur.matrixU() * m_eivec; + m_isInitialized = true; + + // sort the eigenvalues + { + for (int i=0; i +// +// 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 . + +#ifndef EIGEN_COMPLEX_SCHUR_H +#define EIGEN_COMPLEX_SCHUR_H + +/** \eigenvalues_module \ingroup Eigenvalues_Module + * \nonstableyet + * + * \class ComplexShur + * + * \brief Performs a complex Shur decomposition of a real or complex square matrix + * + */ +template class ComplexSchur +{ + public: + typedef _MatrixType MatrixType; + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits::Real RealScalar; + typedef std::complex Complex; + typedef Matrix ComplexMatrixType; + + /** + * \brief Default Constructor. + * + * The default constructor is useful in cases in which the user intends to + * perform decompositions via ComplexSchur::compute(const MatrixType&). + */ + ComplexSchur() : m_matT(), m_matU(), m_isInitialized(false) + {} + + ComplexSchur(const MatrixType& matrix) + : m_matT(matrix.rows(),matrix.cols()), + m_matU(matrix.rows(),matrix.cols()), + m_isInitialized(false) + { + compute(matrix); + } + + ComplexMatrixType matrixU() const + { + ei_assert(m_isInitialized && "ComplexSchur is not initialized."); + return m_matU; + } + + ComplexMatrixType matrixT() const + { + ei_assert(m_isInitialized && "ComplexShur is not initialized."); + return m_matT; + } + + void compute(const MatrixType& matrix); + + protected: + ComplexMatrixType m_matT, m_matU; + bool m_isInitialized; +}; + +/** Computes the principal value of the square root of the complex \a z. */ +template +std::complex ei_sqrt(const std::complex &z) +{ + RealScalar t, tre, tim; + + t = ei_abs(z); + + if (ei_abs(ei_real(z)) <= ei_abs(ei_imag(z))) + { + // No cancellation in these formulas + tre = ei_sqrt(0.5*(t + ei_real(z))); + tim = ei_sqrt(0.5*(t - ei_real(z))); + } + else + { + // Stable computation of the above formulas + if (z.real() > 0) + { + tre = t + z.real(); + tim = ei_abs(ei_imag(z))*ei_sqrt(0.5/tre); + tre = ei_sqrt(0.5*tre); + } + else + { + tim = t - z.real(); + tre = ei_abs(ei_imag(z))*ei_sqrt(0.5/tim); + tim = ei_sqrt(0.5*tim); + } + } + if(z.imag() < 0) + tim = -tim; + + return (std::complex(tre,tim)); + +} + +template +void ComplexSchur::compute(const MatrixType& matrix) +{ + // this code is inspired from Jampack + assert(matrix.cols() == matrix.rows()); + int n = matrix.cols(); + + // Reduce to Hessenberg form + HessenbergDecomposition hess(matrix); + + m_matT = hess.matrixH(); + m_matU = hess.matrixQ(); + + int iu = m_matT.cols() - 1; + int il; + RealScalar d,sd,sf; + Complex c,b,disc,r1,r2,kappa; + + RealScalar eps = epsilon(); + + int iter = 0; + while(true) + { + //locate the range in which to iterate + while(iu > 0) + { + d = ei_norm1(m_matT.coeffRef(iu,iu)) + ei_norm1(m_matT.coeffRef(iu-1,iu-1)); + sd = ei_norm1(m_matT.coeffRef(iu,iu-1)); + + if(sd >= eps * d) break; // FIXME : precision criterion ?? + + m_matT.coeffRef(iu,iu-1) = Complex(0); + iter = 0; + --iu; + } + if(iu==0) break; + iter++; + + if(iter >= 30) + { + // FIXME : what to do when iter==MAXITER ?? + std::cerr << "MAXITER" << std::endl; + return; + } + + il = iu-1; + while( il > 0 ) + { + // check if the current 2x2 block on the diagonal is upper triangular + d = ei_norm1(m_matT.coeffRef(il,il)) + ei_norm1(m_matT.coeffRef(il-1,il-1)); + sd = ei_norm1(m_matT.coeffRef(il,il-1)); + + if(sd < eps * d) break; // FIXME : precision criterion ?? + + --il; + } + + if( il != 0 ) m_matT.coeffRef(il,il-1) = Complex(0); + + // compute the shift (the normalization by sf is to avoid under/overflow) + Matrix t = m_matT.template block<2,2>(iu-1,iu-1); + sf = t.cwise().abs().sum(); + t /= sf; + + c = t.determinant(); + b = t.diagonal().sum(); + + disc = ei_sqrt(b*b - RealScalar(4)*c); + + 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; + + // perform the QR step using Givens rotations + PlanarRotation rot; + rot.makeGivens(m_matT.coeff(il,il) - kappa, m_matT.coeff(il+1,il)); + + for(int i=il ; i +// +// 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 . + +#ifndef EIGEN_EIGENSOLVER_H +#define EIGEN_EIGENSOLVER_H + +/** \eigenvalues_module \ingroup Eigenvalues_Module + * \nonstableyet + * + * \class EigenSolver + * + * \brief Eigen values/vectors solver for non selfadjoint matrices + * + * \param MatrixType the type of the matrix of which we are computing the eigen decomposition + * + * Currently it only support real matrices. + * + * \note this code was adapted from JAMA (public domain) + * + * \sa MatrixBase::eigenvalues(), SelfAdjointEigenSolver + */ +template class EigenSolver +{ + public: + + typedef _MatrixType MatrixType; + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits::Real RealScalar; + typedef std::complex Complex; + typedef Matrix EigenvalueType; + typedef Matrix EigenvectorType; + typedef Matrix RealVectorType; + typedef Matrix RealVectorTypeX; + + /** + * \brief Default Constructor. + * + * The default constructor is useful in cases in which the user intends to + * perform decompositions via EigenSolver::compute(const MatrixType&). + */ + EigenSolver() : m_eivec(), m_eivalues(), m_isInitialized(false) {} + + EigenSolver(const MatrixType& matrix) + : m_eivec(matrix.rows(), matrix.cols()), + m_eivalues(matrix.cols()), + m_isInitialized(false) + { + compute(matrix); + } + + + EigenvectorType eigenvectors(void) const; + + /** \returns a real matrix V of pseudo eigenvectors. + * + * 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. + * + * 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$ + * + * \sa pseudoEigenvalueMatrix() + */ + const MatrixType& pseudoEigenvectors() const + { + ei_assert(m_isInitialized && "EigenSolver is not initialized."); + return m_eivec; + } + + MatrixType pseudoEigenvalueMatrix() const; + + /** \returns the eigenvalues as a column vector */ + EigenvalueType eigenvalues() const + { + ei_assert(m_isInitialized && "EigenSolver is not initialized."); + return m_eivalues; + } + + EigenSolver& compute(const MatrixType& matrix); + + private: + + void orthes(MatrixType& matH, RealVectorType& ort); + void hqr2(MatrixType& matH); + + protected: + MatrixType m_eivec; + EigenvalueType m_eivalues; + bool m_isInitialized; +}; + +/** \returns the real block diagonal matrix D of the eigenvalues. + * + * See pseudoEigenvectors() for the details. + */ +template +MatrixType EigenSolver::pseudoEigenvalueMatrix() const +{ + ei_assert(m_isInitialized && "EigenSolver is not initialized."); + int n = m_eivec.cols(); + MatrixType matD = MatrixType::Zero(n,n); + for (int i=0; i(i,i) << ei_real(m_eivalues.coeff(i)), ei_imag(m_eivalues.coeff(i)), + -ei_imag(m_eivalues.coeff(i)), ei_real(m_eivalues.coeff(i)); + ++i; + } + } + return matD; +} + +/** \returns the normalized complex eigenvectors as a matrix of column vectors. + * + * \sa eigenvalues(), pseudoEigenvectors() + */ +template +typename EigenSolver::EigenvectorType EigenSolver::eigenvectors(void) const +{ + ei_assert(m_isInitialized && "EigenSolver is not initialized."); + int n = m_eivec.cols(); + EigenvectorType matV(n,n); + for (int j=0; j(); + } + else + { + // we have a pair of complex eigen values + for (int i=0; i +EigenSolver& EigenSolver::compute(const MatrixType& matrix) +{ + assert(matrix.cols() == matrix.rows()); + int n = matrix.cols(); + m_eivalues.resize(n,1); + + MatrixType matH = matrix; + RealVectorType ort(n); + + // Reduce to Hessenberg form. + orthes(matH, ort); + + // Reduce Hessenberg to real Schur form. + hqr2(matH); + + m_isInitialized = true; + return *this; +} + +// Nonsymmetric reduction to Hessenberg form. +template +void EigenSolver::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).cwise().abs().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 +std::complex cdiv(Scalar xr, Scalar xi, Scalar yr, Scalar yi) +{ + Scalar r,d; + if (ei_abs(yr) > ei_abs(yi)) + { + r = yi/yr; + d = yr + r*yi; + return std::complex((xr + r*xi)/d, (xi - r*xr)/d); + } + else + { + r = yr/yi; + d = yi + r*yr; + return std::complex((r*xr + xi)/d, (r*xi - xr)/d); + } +} + + +// Nonsymmetric reduction from Hessenberg to real Schur form. +template +void EigenSolver::hqr2(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. + + // 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::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().cwise().abs().sum() + matH.corner(BottomLeft,n,n).diagonal().cwise().abs().sum(); + 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)).cwise().abs().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--) + { + p = m_eivalues.coeff(n).real(); + q = m_eivalues.coeff(n).imag(); + + // Scalar vector + if (q == 0) + { + int l = n; + matH.coeffRef(n,n) = 1.0; + for (int i = n-1; i >= 0; i--) + { + w = matH.coeff(i,i) - p; + r = matH.row(i).segment(l,n-l+1).dot(matH.col(n).segment(l, n-l+1)); + + if (m_eivalues.coeff(i).imag() < 0.0) + { + z = w; + s = r; + } + else + { + l = i; + if (m_eivalues.coeff(i).imag() == 0.0) + { + if (w != 0.0) + matH.coeffRef(i,n) = -r / w; + else + matH.coeffRef(i,n) = -r / (eps * norm); + } + else // Solve real equations + { + x = matH.coeff(i,i+1); + y = matH.coeff(i+1,i); + q = (m_eivalues.coeff(i).real() - p) * (m_eivalues.coeff(i).real() - p) + m_eivalues.coeff(i).imag() * m_eivalues.coeff(i).imag(); + t = (x * s - z * r) / q; + matH.coeffRef(i,n) = t; + if (ei_abs(x) > ei_abs(z)) + matH.coeffRef(i+1,n) = (-r - w * t) / x; + else + matH.coeffRef(i+1,n) = (-s - y * t) / z; + } + + // Overflow control + t = ei_abs(matH.coeff(i,n)); + if ((eps * t) * t > 1) + matH.col(n).end(nn-i) /= t; + } + } + } + else if (q < 0) // Complex vector + { + std::complex cc; + int l = n-1; + + // Last vector component imaginary so matrix is triangular + if (ei_abs(matH.coeff(n,n-1)) > ei_abs(matH.coeff(n-1,n))) + { + matH.coeffRef(n-1,n-1) = q / matH.coeff(n,n-1); + matH.coeffRef(n-1,n) = -(matH.coeff(n,n) - p) / matH.coeff(n,n-1); + } + else + { + cc = cdiv(0.0,-matH.coeff(n-1,n),matH.coeff(n-1,n-1)-p,q); + matH.coeffRef(n-1,n-1) = ei_real(cc); + matH.coeffRef(n-1,n) = ei_imag(cc); + } + matH.coeffRef(n,n-1) = 0.0; + matH.coeffRef(n,n) = 1.0; + for (int i = n-2; i >= 0; i--) + { + Scalar ra,sa,vr,vi; + ra = matH.row(i).segment(l, n-l+1).dot(matH.col(n-1).segment(l, n-l+1)); + sa = matH.row(i).segment(l, n-l+1).dot(matH.col(n).segment(l, n-l+1)); + w = matH.coeff(i,i) - p; + + if (m_eivalues.coeff(i).imag() < 0.0) + { + z = w; + r = ra; + s = sa; + } + else + { + l = i; + if (m_eivalues.coeff(i).imag() == 0) + { + cc = cdiv(-ra,-sa,w,q); + matH.coeffRef(i,n-1) = ei_real(cc); + matH.coeffRef(i,n) = ei_imag(cc); + } + else + { + // Solve complex equations + x = matH.coeff(i,i+1); + y = matH.coeff(i+1,i); + vr = (m_eivalues.coeff(i).real() - p) * (m_eivalues.coeff(i).real() - p) + m_eivalues.coeff(i).imag() * m_eivalues.coeff(i).imag() - q * q; + vi = (m_eivalues.coeff(i).real() - p) * Scalar(2) * q; + if ((vr == 0.0) && (vi == 0.0)) + vr = eps * norm * (ei_abs(w) + ei_abs(q) + ei_abs(x) + ei_abs(y) + ei_abs(z)); + + cc= cdiv(x*r-z*ra+q*sa,x*s-z*sa-q*ra,vr,vi); + matH.coeffRef(i,n-1) = ei_real(cc); + matH.coeffRef(i,n) = ei_imag(cc); + if (ei_abs(x) > (ei_abs(z) + ei_abs(q))) + { + matH.coeffRef(i+1,n-1) = (-ra - w * matH.coeff(i,n-1) + q * matH.coeff(i,n)) / x; + matH.coeffRef(i+1,n) = (-sa - w * matH.coeff(i,n) - q * matH.coeff(i,n-1)) / x; + } + else + { + cc = cdiv(-r-y*matH.coeff(i,n-1),-s-y*matH.coeff(i,n),z,q); + matH.coeffRef(i+1,n-1) = ei_real(cc); + matH.coeffRef(i+1,n) = ei_imag(cc); + } + } + + // Overflow control + t = std::max(ei_abs(matH.coeff(i,n-1)),ei_abs(matH.coeff(i,n))); + if ((eps * t) * t > 1) + matH.block(i, n-1, nn-i, 2) /= t; + + } + } + } + } + + // Vectors of isolated roots + for (int i = 0; i < nn; ++i) + { + // FIXME again what's the purpose of this test ? + // in this algo low==0 and high==nn-1 !! + if (i < low || i > high) + { + m_eivec.row(i).end(nn-i) = matH.row(i).end(nn-i); + } + } + + // Back transformation to get eigenvectors of original matrix + int bRows = high-low+1; + for (int j = nn-1; j >= low; j--) + { + int bSize = std::min(j,high)-low+1; + m_eivec.col(j).segment(low, bRows) = (m_eivec.block(low, low, bRows, bSize) * matH.col(j).segment(low, bSize)); + } +} + +#endif // EIGEN_EIGENSOLVER_H diff --git a/Eigen/src/Eigenvalues/HessenbergDecomposition.h b/Eigen/src/Eigenvalues/HessenbergDecomposition.h new file mode 100644 index 000000000..b1e21d4ee --- /dev/null +++ b/Eigen/src/Eigenvalues/HessenbergDecomposition.h @@ -0,0 +1,206 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008 Gael Guennebaud +// +// 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 . + +#ifndef EIGEN_HESSENBERGDECOMPOSITION_H +#define EIGEN_HESSENBERGDECOMPOSITION_H + +/** \eigenvalues_module \ingroup Eigenvalues_Module + * \nonstableyet + * + * \class HessenbergDecomposition + * + * \brief Reduces a squared matrix to an Hessemberg form + * + * \param 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. + * + * \sa class Tridiagonalization, class Qr + */ +template class HessenbergDecomposition +{ + public: + + typedef _MatrixType MatrixType; + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits::Real RealScalar; + + enum { + Size = MatrixType::RowsAtCompileTime, + SizeMinusOne = MatrixType::RowsAtCompileTime==Dynamic + ? Dynamic + : MatrixType::RowsAtCompileTime-1 + }; + + typedef Matrix CoeffVectorType; + typedef Matrix DiagonalType; + typedef Matrix SubDiagonalType; + + typedef typename NestByValue >::RealReturnType DiagonalReturnType; + + typedef typename NestByValue >,0 > >::RealReturnType SubDiagonalReturnType; + + /** This constructor initializes a HessenbergDecomposition object for + * further use with HessenbergDecomposition::compute() + */ + HessenbergDecomposition(int size = Size==Dynamic ? 2 : Size) + : m_matrix(size,size), m_hCoeffs(size-1) + {} + + HessenbergDecomposition(const MatrixType& matrix) + : m_matrix(matrix), + m_hCoeffs(matrix.cols()-1) + { + _compute(m_matrix, m_hCoeffs); + } + + /** Computes or re-compute the Hessenberg decomposition for the matrix \a matrix. + * + * This method allows to re-use the allocated data. + */ + void compute(const MatrixType& matrix) + { + m_matrix = matrix; + m_hCoeffs.resize(matrix.rows()-1,1); + _compute(m_matrix, m_hCoeffs); + } + + /** \returns the householder coefficients allowing to + * reconstruct the matrix Q from the packed data. + * + * \sa packedMatrix() + */ + CoeffVectorType householderCoefficients() const { return m_hCoeffs; } + + /** \returns the internal result of the decomposition. + * + * 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) ] + * + * See LAPACK for further details on this packed storage. + */ + const MatrixType& packedMatrix(void) const { return m_matrix; } + + MatrixType matrixQ() const; + MatrixType matrixH() const; + + private: + + static void _compute(MatrixType& matA, CoeffVectorType& hCoeffs); + + protected: + MatrixType m_matrix; + CoeffVectorType m_hCoeffs; +}; + +#ifndef EIGEN_HIDE_HEAVY_CODE + +/** \internal + * Performs a tridiagonal decomposition of \a matA in place. + * + * \param matA the input selfadjoint matrix + * \param hCoeffs returned Householder coefficients + * + * The result is written in the lower triangular part of \a matA. + * + * Implemented from Golub's "Matrix Computations", algorithm 8.3.1. + * + * \sa packedMatrix() + */ +template +void HessenbergDecomposition::_compute(MatrixType& matA, CoeffVectorType& hCoeffs) +{ + assert(matA.rows()==matA.cols()); + int n = matA.rows(); + Matrix temp(n); + for (int i = 0; i +typename HessenbergDecomposition::MatrixType +HessenbergDecomposition::matrixQ() const +{ + int n = m_matrix.rows(); + MatrixType matQ = MatrixType::Identity(n,n); + Matrix temp(n); + for (int i = n-2; i>=0; i--) + { + matQ.corner(BottomRight,n-i-1,n-i-1) + .applyHouseholderOnTheLeft(m_matrix.col(i).end(n-i-2), ei_conj(m_hCoeffs.coeff(i)), &temp.coeffRef(0,0)); + } + return matQ; +} + +#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 HessenbergDecomposition::MatrixType +HessenbergDecomposition::matrixH() const +{ + // FIXME should this function (and other similar) rather take a matrix as argument + // and fill it (to avoid temporaries) + int n = m_matrix.rows(); + MatrixType matH = m_matrix; + if (n>2) + matH.corner(BottomLeft,n-2, n-2).template triangularView().setZero(); + return matH; +} + +#endif // EIGEN_HESSENBERGDECOMPOSITION_H diff --git a/Eigen/src/Eigenvalues/SelfAdjointEigenSolver.h b/Eigen/src/Eigenvalues/SelfAdjointEigenSolver.h new file mode 100644 index 000000000..84856aa66 --- /dev/null +++ b/Eigen/src/Eigenvalues/SelfAdjointEigenSolver.h @@ -0,0 +1,366 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008 Gael Guennebaud +// +// 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 . + +#ifndef EIGEN_SELFADJOINTEIGENSOLVER_H +#define EIGEN_SELFADJOINTEIGENSOLVER_H + +/** \eigenvalues_module \ingroup Eigenvalues_Module + * \nonstableyet + * + * \class SelfAdjointEigenSolver + * + * \brief Eigen values/vectors solver for selfadjoint matrix + * + * \param MatrixType the type of the matrix of which we are computing the eigen decomposition + * + * \note MatrixType must be an actual Matrix type, it can't be an expression type. + * + * \sa MatrixBase::eigenvalues(), class EigenSolver + */ +template class SelfAdjointEigenSolver +{ + public: + + enum {Size = _MatrixType::RowsAtCompileTime }; + typedef _MatrixType MatrixType; + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits::Real RealScalar; + typedef std::complex Complex; + typedef Matrix RealVectorType; + typedef Matrix RealVectorTypeX; + typedef Tridiagonalization TridiagonalizationType; +// typedef typename TridiagonalizationType::TridiagonalMatrixType TridiagonalMatrixType; + + SelfAdjointEigenSolver() + : m_eivec(int(Size), int(Size)), + m_eivalues(int(Size)) + { + ei_assert(Size!=Dynamic); + } + + SelfAdjointEigenSolver(int size) + : m_eivec(size, size), + m_eivalues(size) + {} + + /** Constructors computing the eigenvalues of the selfadjoint matrix \a matrix, + * as well as the eigenvectors if \a computeEigenvectors is true. + * + * \sa compute(MatrixType,bool), SelfAdjointEigenSolver(MatrixType,MatrixType,bool) + */ + SelfAdjointEigenSolver(const MatrixType& matrix, bool computeEigenvectors = true) + : m_eivec(matrix.rows(), matrix.cols()), + m_eivalues(matrix.cols()) + { + compute(matrix, computeEigenvectors); + } + + /** Constructors computing the eigenvalues of the generalized eigen problem + * \f$ Ax = lambda B x \f$ with \a matA the selfadjoint matrix \f$ A \f$ + * and \a matB the positive definite matrix \f$ B \f$ . The eigenvectors + * are computed if \a computeEigenvectors is true. + * + * \sa compute(MatrixType,MatrixType,bool), SelfAdjointEigenSolver(MatrixType,bool) + */ + SelfAdjointEigenSolver(const MatrixType& matA, const MatrixType& matB, bool computeEigenvectors = true) + : m_eivec(matA.rows(), matA.cols()), + m_eivalues(matA.cols()) + { + compute(matA, matB, computeEigenvectors); + } + + SelfAdjointEigenSolver& compute(const MatrixType& matrix, bool computeEigenvectors = true); + + SelfAdjointEigenSolver& compute(const MatrixType& matA, const MatrixType& matB, bool computeEigenvectors = true); + + /** \returns the computed eigen vectors as a matrix of column vectors */ + MatrixType eigenvectors(void) const + { + #ifndef NDEBUG + ei_assert(m_eigenvectorsOk); + #endif + return m_eivec; + } + + /** \returns the computed eigen values */ + RealVectorType eigenvalues(void) const { return m_eivalues; } + + /** \returns the positive square root of the matrix + * + * \note the matrix itself must be positive in order for this to make sense. + */ + MatrixType operatorSqrt() const + { + return m_eivec * m_eivalues.cwise().sqrt().asDiagonal() * m_eivec.adjoint(); + } + + /** \returns the positive inverse square root of the matrix + * + * \note the matrix itself must be positive definite in order for this to make sense. + */ + MatrixType operatorInverseSqrt() const + { + return m_eivec * m_eivalues.cwise().inverse().cwise().sqrt().asDiagonal() * m_eivec.adjoint(); + } + + + protected: + MatrixType m_eivec; + RealVectorType m_eivalues; + #ifndef NDEBUG + bool m_eigenvectorsOk; + #endif +}; + +#ifndef EIGEN_HIDE_HEAVY_CODE + +/** \internal + * + * \eigenvalues_module \ingroup Eigenvalues_Module + * + * Performs a QR step on a tridiagonal symmetric matrix represented as a + * pair of two vectors \a diag and \a subdiag. + * + * \param matA the input selfadjoint matrix + * \param hCoeffs returned Householder coefficients + * + * For compilation efficiency reasons, this procedure does not use eigen expression + * for its arguments. + * + * Implemented from Golub's "Matrix Computations", algorithm 8.3.2: + * "implicit symmetric QR step with Wilkinson shift" + */ +template +static void ei_tridiagonal_qr_step(RealScalar* diag, RealScalar* subdiag, int start, int end, Scalar* matrixQ, int n); + +/** Computes the eigenvalues of the selfadjoint matrix \a matrix, + * as well as the eigenvectors if \a computeEigenvectors is true. + * + * \sa SelfAdjointEigenSolver(MatrixType,bool), compute(MatrixType,MatrixType,bool) + */ +template +SelfAdjointEigenSolver& SelfAdjointEigenSolver::compute(const MatrixType& matrix, bool computeEigenvectors) +{ + #ifndef NDEBUG + m_eigenvectorsOk = computeEigenvectors; + #endif + assert(matrix.cols() == matrix.rows()); + int n = matrix.cols(); + m_eivalues.resize(n,1); + + if(n==1) + { + m_eivalues.coeffRef(0,0) = ei_real(matrix.coeff(0,0)); + m_eivec.setOnes(); + return *this; + } + + m_eivec = matrix; + + // FIXME, should tridiag be a local variable of this function or an attribute of SelfAdjointEigenSolver ? + // the latter avoids multiple memory allocation when the same SelfAdjointEigenSolver is used multiple times... + // (same for diag and subdiag) + RealVectorType& diag = m_eivalues; + typename TridiagonalizationType::SubDiagonalType subdiag(n-1); + TridiagonalizationType::decomposeInPlace(m_eivec, diag, subdiag, computeEigenvectors); + + int end = n-1; + int start = 0; + while (end>0) + { + for (int i = start; i0 && subdiag[end-1]==0) + end--; + if (end<=0) + break; + start = end - 1; + while (start>0 && subdiag[start-1]!=0) + start--; + + ei_tridiagonal_qr_step(diag.data(), subdiag.data(), start, end, computeEigenvectors ? m_eivec.data() : (Scalar*)0, n); + } + + // Sort eigenvalues and corresponding vectors. + // TODO make the sort optional ? + // TODO use a better sort algorithm !! + for (int i = 0; i < n-1; ++i) + { + int k; + m_eivalues.segment(i,n-i).minCoeff(&k); + if (k > 0) + { + std::swap(m_eivalues[i], m_eivalues[k+i]); + m_eivec.col(i).swap(m_eivec.col(k+i)); + } + } + return *this; +} + +/** Computes the eigenvalues of the generalized eigen problem + * \f$ Ax = lambda B x \f$ with \a matA the selfadjoint matrix \f$ A \f$ + * and \a matB the positive definite matrix \f$ B \f$ . The eigenvectors + * are computed if \a computeEigenvectors is true. + * + * \sa SelfAdjointEigenSolver(MatrixType,MatrixType,bool), compute(MatrixType,bool) + */ +template +SelfAdjointEigenSolver& SelfAdjointEigenSolver:: +compute(const MatrixType& matA, const MatrixType& matB, bool computeEigenvectors) +{ + ei_assert(matA.cols()==matA.rows() && matB.rows()==matA.rows() && matB.cols()==matB.rows()); + + // Compute the cholesky decomposition of matB = L L' + LLT cholB(matB); + + // compute C = inv(L) A inv(L') + MatrixType matC = matA; + cholB.matrixL().solveInPlace(matC); + // FIXME since we currently do not support A * inv(L'), let's do (inv(L) A')' : + matC.adjointInPlace(); + cholB.matrixL().solveInPlace(matC); + matC.adjointInPlace(); + // this version works too: +// matC = matC.transpose(); +// cholB.matrixL().conjugate().template marked().solveTriangularInPlace(matC); +// matC = matC.transpose(); + // FIXME: this should work: (currently it only does for small matrices) +// Transpose trMatC(matC); +// cholB.matrixL().conjugate().eval().template marked().solveTriangularInPlace(trMatC); + + compute(matC, computeEigenvectors); + + if (computeEigenvectors) + { + // transform back the eigen vectors: evecs = inv(U) * evecs + cholB.matrixU().solveInPlace(m_eivec); + for (int i=0; i +inline Matrix::Scalar>::Real, ei_traits::ColsAtCompileTime, 1> +MatrixBase::eigenvalues() const +{ + ei_assert(Flags&SelfAdjointBit); + return SelfAdjointEigenSolver(eval(),false).eigenvalues(); +} + +template +struct ei_operatorNorm_selector +{ + static inline typename NumTraits::Scalar>::Real + operatorNorm(const MatrixBase& m) + { + // FIXME if it is really guaranteed that the eigenvalues are already sorted, + // then we don't need to compute a maxCoeff() here, comparing the 1st and last ones is enough. + return m.eigenvalues().cwise().abs().maxCoeff(); + } +}; + +template struct ei_operatorNorm_selector +{ + static inline typename NumTraits::Scalar>::Real + operatorNorm(const MatrixBase& m) + { + typename Derived::PlainMatrixType m_eval(m); + // FIXME if it is really guaranteed that the eigenvalues are already sorted, + // then we don't need to compute a maxCoeff() here, comparing the 1st and last ones is enough. + return ei_sqrt( + (m_eval*m_eval.adjoint()) + .template marked() + .eigenvalues() + .maxCoeff() + ); + } +}; + +/** \eigenvalues_module + * + * \returns the matrix norm of this matrix. + */ +template +inline typename NumTraits::Scalar>::Real +MatrixBase::operatorNorm() const +{ + return ei_operatorNorm_selector + ::operatorNorm(derived()); +} + +#ifndef EIGEN_EXTERN_INSTANTIATIONS +template +static void ei_tridiagonal_qr_step(RealScalar* diag, RealScalar* subdiag, int start, int end, Scalar* matrixQ, int n) +{ + RealScalar td = (diag[end-1] - diag[end])*RealScalar(0.5); + RealScalar e2 = ei_abs2(subdiag[end-1]); + RealScalar mu = diag[end] - e2 / (td + (td>0 ? 1 : -1) * ei_sqrt(td*td + e2)); + RealScalar x = diag[start] - mu; + RealScalar z = subdiag[start]; + + for (int k = start; k < end; ++k) + { + PlanarRotation rot; + rot.makeGivens(x, z); + + // do T = G' T G + RealScalar sdk = rot.s() * diag[k] + rot.c() * subdiag[k]; + RealScalar dkp1 = rot.s() * subdiag[k] + rot.c() * diag[k+1]; + + diag[k] = rot.c() * (rot.c() * diag[k] - rot.s() * subdiag[k]) - rot.s() * (rot.c() * subdiag[k] - rot.s() * diag[k+1]); + diag[k+1] = rot.s() * sdk + rot.c() * dkp1; + subdiag[k] = rot.c() * sdk - rot.s() * dkp1; + + if (k > start) + subdiag[k - 1] = rot.c() * subdiag[k-1] - rot.s() * z; + + x = subdiag[k]; + + if (k < end - 1) + { + z = -rot.s() * subdiag[k+1]; + subdiag[k + 1] = rot.c() * subdiag[k+1]; + } + + // apply the givens rotation to the unit matrix Q = Q * G + if (matrixQ) + { + Map > q(matrixQ,n,n); + q.applyOnTheRight(k,k+1,rot); + } + } +} +#endif + +#endif // EIGEN_SELFADJOINTEIGENSOLVER_H diff --git a/Eigen/src/Eigenvalues/Tridiagonalization.h b/Eigen/src/Eigenvalues/Tridiagonalization.h new file mode 100644 index 000000000..5f891bfa6 --- /dev/null +++ b/Eigen/src/Eigenvalues/Tridiagonalization.h @@ -0,0 +1,317 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008 Gael Guennebaud +// +// 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 . + +#ifndef EIGEN_TRIDIAGONALIZATION_H +#define EIGEN_TRIDIAGONALIZATION_H + +/** \eigenvalues_module \ingroup Eigenvalues_Module + * \nonstableyet + * + * \class Tridiagonalization + * + * \brief Trigiagonal decomposition of a selfadjoint matrix + * + * \param MatrixType the type of the matrix of which we are performing the tridiagonalization + * + * This class performs a tridiagonal decomposition of a selfadjoint matrix \f$ A \f$ such that: + * \f$ A = Q T Q^* \f$ where \f$ Q \f$ is unitary and \f$ T \f$ a real symmetric tridiagonal matrix. + * + * \sa MatrixBase::tridiagonalize() + */ +template class Tridiagonalization +{ + public: + + typedef _MatrixType MatrixType; + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits::Real RealScalar; + typedef typename ei_packet_traits::type Packet; + + enum { + Size = MatrixType::RowsAtCompileTime, + SizeMinusOne = MatrixType::RowsAtCompileTime==Dynamic + ? Dynamic + : MatrixType::RowsAtCompileTime-1, + PacketSize = ei_packet_traits::size + }; + + typedef Matrix CoeffVectorType; + typedef Matrix DiagonalType; + typedef Matrix SubDiagonalType; + + typedef typename ei_meta_if::IsComplex, + typename NestByValue >::RealReturnType, + Diagonal + >::ret DiagonalReturnType; + + typedef typename ei_meta_if::IsComplex, + typename NestByValue >,0 > >::RealReturnType, + Diagonal< + NestByValue >,0 > + >::ret SubDiagonalReturnType; + + /** This constructor initializes a Tridiagonalization object for + * further use with Tridiagonalization::compute() + */ + Tridiagonalization(int size = Size==Dynamic ? 2 : Size) + : m_matrix(size,size), m_hCoeffs(size-1) + {} + + Tridiagonalization(const MatrixType& matrix) + : m_matrix(matrix), m_hCoeffs(matrix.cols()-1) + { + _compute(m_matrix, m_hCoeffs); + } + + /** Computes or re-compute the tridiagonalization for the matrix \a matrix. + * + * This method allows to re-use the allocated data. + */ + void compute(const MatrixType& matrix) + { + m_matrix = matrix; + m_hCoeffs.resize(matrix.rows()-1, 1); + _compute(m_matrix, m_hCoeffs); + } + + /** \returns the householder coefficients allowing to + * reconstruct the matrix Q from the packed data. + * + * \sa packedMatrix() + */ + inline CoeffVectorType householderCoefficients(void) const { return m_hCoeffs; } + + /** \returns the internal result of the decomposition. + * + * The returned matrix contains the following information: + * - the strict upper part is equal to the input matrix A + * - the diagonal and lower sub-diagonal represent the tridiagonal symmetric matrix (real). + * - 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 transformations: + * 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) ] + * + * See LAPACK for further details on this packed storage. + */ + inline const MatrixType& packedMatrix(void) const { return m_matrix; } + + MatrixType matrixQ() const; + template void matrixQInPlace(MatrixBase* q) const; + MatrixType matrixT() const; + const DiagonalReturnType diagonal(void) const; + const SubDiagonalReturnType subDiagonal(void) const; + + static void decomposeInPlace(MatrixType& mat, DiagonalType& diag, SubDiagonalType& subdiag, bool extractQ = true); + + static void _compute(MatrixType& matA, CoeffVectorType& hCoeffs); + + protected: + + static void _decomposeInPlace3x3(MatrixType& mat, DiagonalType& diag, SubDiagonalType& subdiag, bool extractQ = true); + + MatrixType m_matrix; + CoeffVectorType m_hCoeffs; +}; + +/** \returns an expression of the diagonal vector */ +template +const typename Tridiagonalization::DiagonalReturnType +Tridiagonalization::diagonal(void) const +{ + return m_matrix.diagonal().nestByValue(); +} + +/** \returns an expression of the sub-diagonal vector */ +template +const typename Tridiagonalization::SubDiagonalReturnType +Tridiagonalization::subDiagonal(void) const +{ + int n = m_matrix.rows(); + return Block(m_matrix, 1, 0, n-1,n-1) + .nestByValue().diagonal().nestByValue(); +} + +/** constructs and returns the tridiagonal matrix T. + * Note that the matrix T is equivalent to the diagonal and sub-diagonal of the packed matrix. + * Therefore, it might be often sufficient to directly use the packed matrix, or the vector + * expressions returned by diagonal() and subDiagonal() instead of creating a new matrix. + */ +template +typename Tridiagonalization::MatrixType +Tridiagonalization::matrixT(void) const +{ + // FIXME should this function (and other similar ones) rather take a matrix as argument + // and fill it ? (to avoid temporaries) + int n = m_matrix.rows(); + MatrixType matT = m_matrix; + matT.corner(TopRight,n-1, n-1).diagonal() = subDiagonal().template cast().conjugate(); + if (n>2) + { + matT.corner(TopRight,n-2, n-2).template triangularView().setZero(); + matT.corner(BottomLeft,n-2, n-2).template triangularView().setZero(); + } + return matT; +} + +#ifndef EIGEN_HIDE_HEAVY_CODE + +/** \internal + * Performs a tridiagonal decomposition of \a matA in place. + * + * \param matA the input selfadjoint matrix + * \param hCoeffs returned Householder coefficients + * + * The result is written in the lower triangular part of \a matA. + * + * Implemented from Golub's "Matrix Computations", algorithm 8.3.1. + * + * \sa packedMatrix() + */ +template +void Tridiagonalization::_compute(MatrixType& matA, CoeffVectorType& hCoeffs) +{ + assert(matA.rows()==matA.cols()); + int n = matA.rows(); + Matrix aux(n); + for (int i = 0; i() + * (ei_conj(h) * matA.col(i).end(remainingSize))); + + hCoeffs.end(n-i-1) += (ei_conj(h)*Scalar(-0.5)*(hCoeffs.end(remainingSize).dot(matA.col(i).end(remainingSize)))) * matA.col(i).end(n-i-1); + + matA.corner(BottomRight, remainingSize, remainingSize).template selfadjointView() + .rankUpdate(matA.col(i).end(remainingSize), hCoeffs.end(remainingSize), -1); + + matA.col(i).coeffRef(i+1) = beta; + hCoeffs.coeffRef(i) = h; + } +} + +/** reconstructs and returns the matrix Q */ +template +typename Tridiagonalization::MatrixType +Tridiagonalization::matrixQ(void) const +{ + MatrixType matQ; + matrixQInPlace(&matQ); + return matQ; +} + +template +template +void Tridiagonalization::matrixQInPlace(MatrixBase* q) const +{ + QDerived& matQ = q->derived(); + int n = m_matrix.rows(); + matQ = MatrixType::Identity(n,n); + Matrix aux(n); + for (int i = n-2; i>=0; i--) + { + matQ.corner(BottomRight,n-i-1,n-i-1) + .applyHouseholderOnTheLeft(m_matrix.col(i).end(n-i-2), ei_conj(m_hCoeffs.coeff(i)), &aux.coeffRef(0,0)); + } +} + +/** Performs a full decomposition in place */ +template +void Tridiagonalization::decomposeInPlace(MatrixType& mat, DiagonalType& diag, SubDiagonalType& subdiag, bool extractQ) +{ + int n = mat.rows(); + ei_assert(mat.cols()==n && diag.size()==n && subdiag.size()==n-1); + if (n==3 && (!NumTraits::IsComplex) ) + { + _decomposeInPlace3x3(mat, diag, subdiag, extractQ); + } + else + { + Tridiagonalization tridiag(mat); + diag = tridiag.diagonal(); + subdiag = tridiag.subDiagonal(); + if (extractQ) + tridiag.matrixQInPlace(&mat); + } +} + +/** \internal + * Optimized path for 3x3 matrices. + * Especially useful for plane fitting. + */ +template +void Tridiagonalization::_decomposeInPlace3x3(MatrixType& mat, DiagonalType& diag, SubDiagonalType& subdiag, bool extractQ) +{ + diag[0] = ei_real(mat(0,0)); + RealScalar v1norm2 = ei_abs2(mat(0,2)); + if (ei_isMuchSmallerThan(v1norm2, RealScalar(1))) + { + diag[1] = ei_real(mat(1,1)); + diag[2] = ei_real(mat(2,2)); + subdiag[0] = ei_real(mat(0,1)); + subdiag[1] = ei_real(mat(1,2)); + if (extractQ) + mat.setIdentity(); + } + else + { + RealScalar beta = ei_sqrt(ei_abs2(mat(0,1))+v1norm2); + RealScalar invBeta = RealScalar(1)/beta; + Scalar m01 = mat(0,1) * invBeta; + Scalar m02 = mat(0,2) * invBeta; + Scalar q = RealScalar(2)*m01*mat(1,2) + m02*(mat(2,2) - mat(1,1)); + diag[1] = ei_real(mat(1,1) + m02*q); + diag[2] = ei_real(mat(2,2) - m02*q); + subdiag[0] = beta; + subdiag[1] = ei_real(mat(1,2) - m01 * q); + if (extractQ) + { + mat(0,0) = 1; + mat(0,1) = 0; + mat(0,2) = 0; + mat(1,0) = 0; + mat(1,1) = m01; + mat(1,2) = m02; + mat(2,0) = 0; + mat(2,1) = m02; + mat(2,2) = -m01; + } + } +} + +#endif // EIGEN_HIDE_HEAVY_CODE + +#endif // EIGEN_TRIDIAGONALIZATION_H diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in index bd0df9eb2..5b055ed11 100644 --- a/doc/Doxyfile.in +++ b/doc/Doxyfile.in @@ -212,7 +212,7 @@ ALIASES = "only_for_vectors=This is only for vectors (either row- "svd_module=This is defined in the %SVD module. \code #include \endcode" \ "geometry_module=This is defined in the %Geometry module. \code #include \endcode" \ "leastsquares_module=This is defined in the %LeastSquares module. \code #include \endcode" \ - "eigensolver_module=This is defined in the %EigenSolver module. \code #include \endcode" \ + "eigenvalues_module=This is defined in the %Eigenvalues module. \code #include \endcode" \ "label=\bug" \ "redstar=*" \ "nonstableyet=\warning This is not considered to be part of the stable public API yet. Changes may happen in future releases. See \ref Experimental \"Experimental parts of Eigen\"" diff --git a/test/eigensolver_complex.cpp b/test/eigensolver_complex.cpp index db129c064..38ede7c4a 100644 --- a/test/eigensolver_complex.cpp +++ b/test/eigensolver_complex.cpp @@ -23,7 +23,7 @@ // Eigen. If not, see . #include "main.h" -#include +#include #include template void eigensolver(const MatrixType& m) diff --git a/test/eigensolver_generic.cpp b/test/eigensolver_generic.cpp index 68ee447e9..e2b2055b4 100644 --- a/test/eigensolver_generic.cpp +++ b/test/eigensolver_generic.cpp @@ -23,7 +23,7 @@ // Eigen. If not, see . #include "main.h" -#include +#include #ifdef HAS_GSL #include "gsl_helper.h" diff --git a/test/eigensolver_selfadjoint.cpp b/test/eigensolver_selfadjoint.cpp index 61d8f1cb5..3836c074b 100644 --- a/test/eigensolver_selfadjoint.cpp +++ b/test/eigensolver_selfadjoint.cpp @@ -23,7 +23,7 @@ // Eigen. If not, see . #include "main.h" -#include +#include #ifdef HAS_GSL #include "gsl_helper.h" diff --git a/test/mixingtypes.cpp b/test/mixingtypes.cpp index 52e68aba2..690442a02 100644 --- a/test/mixingtypes.cpp +++ b/test/mixingtypes.cpp @@ -95,7 +95,7 @@ void mixingtypes_large(int size) VERIFY_RAISES_ASSERT(mf*vcf); VERIFY_RAISES_ASSERT(mcf*vf); VERIFY_RAISES_ASSERT(mcf *= mf); - // VERIFY_RAISES_ASSERT(vcd = md*vcd); // does not even compile + // VERIFY_RAISES_ASSERT(vcd = md*vcd); // does not even compile (cannot convert complex to double) VERIFY_RAISES_ASSERT(vcf = mcf*vf); VERIFY_RAISES_ASSERT(mf*md); -- cgit v1.2.3 From 3fbf71d6b95c11729c673d11aa6d193faae4589e Mon Sep 17 00:00:00 2001 From: Gael Guennebaud Date: Fri, 4 Sep 2009 09:26:00 +0200 Subject: compilation fix for conservativeResize --- Eigen/src/Core/Matrix.h | 121 ++++++++++++++++++++++++------------------------ 1 file changed, 61 insertions(+), 60 deletions(-) diff --git a/Eigen/src/Core/Matrix.h b/Eigen/src/Core/Matrix.h index 70862ad11..0975b3b77 100644 --- a/Eigen/src/Core/Matrix.h +++ b/Eigen/src/Core/Matrix.h @@ -126,6 +126,7 @@ class Matrix EIGEN_GENERIC_PUBLIC_INTERFACE(Matrix) enum { Options = _Options }; + typedef typename Base::PlainMatrixType PlainMatrixType; friend class Eigen::Map; typedef class Eigen::Map UnalignedMapType; friend class Eigen::Map; @@ -321,65 +322,65 @@ class Matrix else resize(other.rows(), other.cols()); } - /** Resizes \c *this to a \a rows x \a cols matrix while leaving old values of *this untouched. - * - * This method is intended for dynamic-size matrices, although it is legal to call it on any - * matrix as long as fixed dimensions are left unchanged. If you only want to change the number - * of rows and/or of columns, you can use conservativeResize(NoChange_t, int), - * conservativeResize(int, NoChange_t). - * - * The top-left part of the resized matrix will be the same as the overlapping top-left corner - * of *this. In case values need to be appended to the matrix they will be uninitialized per - * default and set to zero when init_with_zero is set to true. - */ - inline void conservativeResize(int rows, int cols, bool init_with_zero = false) - { - // Note: Here is space for improvement. Basically, for conservativeResize(int,int), - // neither RowsAtCompileTime or ColsAtCompileTime must be Dynamic. If only one of the - // dimensions is dynamic, one could use either conservativeResize(int rows, NoChange_t) or - // conservativeResize(NoChange_t, int cols). For these methods new static asserts like - // EIGEN_STATIC_ASSERT_DYNAMIC_ROWS and EIGEN_STATIC_ASSERT_DYNAMIC_COLS would be good. - EIGEN_STATIC_ASSERT_DYNAMIC_SIZE(Matrix) - PlainMatrixType tmp = init_with_zero ? PlainMatrixType::Zero(rows, cols) : PlainMatrixType(rows,cols); - const int common_rows = std::min(rows, this->rows()); - const int common_cols = std::min(cols, this->cols()); - tmp.block(0,0,common_rows,common_cols) = this->block(0,0,common_rows,common_cols); - this->derived().swap(tmp); - } - - EIGEN_STRONG_INLINE void conservativeResize(int rows, NoChange_t, bool init_with_zero = false) - { - // Note: see the comment in conservativeResize(int,int,bool) - conservativeResize(rows, cols(), init_with_zero); - } - - EIGEN_STRONG_INLINE void conservativeResize(NoChange_t, int cols, bool init_with_zero = false) - { - // Note: see the comment in conservativeResize(int,int,bool) - conservativeResize(rows(), cols, init_with_zero); - } - - /** Resizes \c *this to a vector of length \a size while retaining old values of *this. - * - * \only_for_vectors. This method does not work for - * partially dynamic matrices when the static dimension is anything other - * than 1. For example it will not work with Matrix. - * - * When values are appended, they will be uninitialized per default and set - * to zero when init_with_zero is set to true. - */ - inline void conservativeResize(int size, bool init_with_zero = false) - { - EIGEN_STATIC_ASSERT_VECTOR_ONLY(Matrix) - EIGEN_STATIC_ASSERT_DYNAMIC_SIZE(Matrix) - - if (RowsAtCompileTime == 1 || ColsAtCompileTime == 1) - { - PlainMatrixType tmp = init_with_zero ? PlainMatrixType::Zero(size) : PlainMatrixType(size); - const int common_size = std::min(this->size(),size); - tmp.segment(0,common_size) = this->segment(0,common_size); - this->derived().swap(tmp); - } + /** Resizes \c *this to a \a rows x \a cols matrix while leaving old values of *this untouched. + * + * This method is intended for dynamic-size matrices, although it is legal to call it on any + * matrix as long as fixed dimensions are left unchanged. If you only want to change the number + * of rows and/or of columns, you can use conservativeResize(NoChange_t, int), + * conservativeResize(int, NoChange_t). + * + * The top-left part of the resized matrix will be the same as the overlapping top-left corner + * of *this. In case values need to be appended to the matrix they will be uninitialized per + * default and set to zero when init_with_zero is set to true. + */ + inline void conservativeResize(int rows, int cols, bool init_with_zero = false) + { + // Note: Here is space for improvement. Basically, for conservativeResize(int,int), + // neither RowsAtCompileTime or ColsAtCompileTime must be Dynamic. If only one of the + // dimensions is dynamic, one could use either conservativeResize(int rows, NoChange_t) or + // conservativeResize(NoChange_t, int cols). For these methods new static asserts like + // EIGEN_STATIC_ASSERT_DYNAMIC_ROWS and EIGEN_STATIC_ASSERT_DYNAMIC_COLS would be good. + EIGEN_STATIC_ASSERT_DYNAMIC_SIZE(Matrix) + PlainMatrixType tmp = init_with_zero ? PlainMatrixType::Zero(rows, cols) : PlainMatrixType(rows,cols); + const int common_rows = std::min(rows, this->rows()); + const int common_cols = std::min(cols, this->cols()); + tmp.block(0,0,common_rows,common_cols) = this->block(0,0,common_rows,common_cols); + this->derived().swap(tmp); + } + + EIGEN_STRONG_INLINE void conservativeResize(int rows, NoChange_t, bool init_with_zero = false) + { + // Note: see the comment in conservativeResize(int,int,bool) + conservativeResize(rows, cols(), init_with_zero); + } + + EIGEN_STRONG_INLINE void conservativeResize(NoChange_t, int cols, bool init_with_zero = false) + { + // Note: see the comment in conservativeResize(int,int,bool) + conservativeResize(rows(), cols, init_with_zero); + } + + /** Resizes \c *this to a vector of length \a size while retaining old values of *this. + * + * \only_for_vectors. This method does not work for + * partially dynamic matrices when the static dimension is anything other + * than 1. For example it will not work with Matrix. + * + * When values are appended, they will be uninitialized per default and set + * to zero when init_with_zero is set to true. + */ + inline void conservativeResize(int size, bool init_with_zero = false) + { + EIGEN_STATIC_ASSERT_VECTOR_ONLY(Matrix) + EIGEN_STATIC_ASSERT_DYNAMIC_SIZE(Matrix) + + if (RowsAtCompileTime == 1 || ColsAtCompileTime == 1) + { + PlainMatrixType tmp = init_with_zero ? PlainMatrixType::Zero(size) : PlainMatrixType(size); + const int common_size = std::min(this->size(),size); + tmp.segment(0,common_size) = this->segment(0,common_size); + this->derived().swap(tmp); + } } /** Copies the value of the expression \a other into \c *this with automatic resizing. @@ -711,7 +712,7 @@ class Matrix m_storage.data()[0] = x; m_storage.data()[1] = y; } - + template friend struct ei_matrix_swap_impl; }; -- cgit v1.2.3 From a7ed998d523287e790142f4d3ff3d7f8e37e4d17 Mon Sep 17 00:00:00 2001 From: Gael Guennebaud Date: Fri, 4 Sep 2009 10:05:22 +0200 Subject: bug fix in novel makeGivens for real --- Eigen/src/Jacobi/Jacobi.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Eigen/src/Jacobi/Jacobi.h b/Eigen/src/Jacobi/Jacobi.h index 7fd57ed5f..3905f4d8f 100644 --- a/Eigen/src/Jacobi/Jacobi.h +++ b/Eigen/src/Jacobi/Jacobi.h @@ -233,7 +233,7 @@ void PlanarRotation::makeGivens(const Scalar& p, const Scalar& q, Scalar m_s = qq) + else if(ei_abs(p) > ei_abs(q)) { Scalar t = q/p; Scalar u = ei_sqrt(Scalar(1) + ei_abs2(t)); @@ -250,7 +250,7 @@ void PlanarRotation::makeGivens(const Scalar& p, const Scalar& q, Scalar if(q Date: Fri, 4 Sep 2009 10:17:28 +0200 Subject: extend mixingtype test to check diagonal products and fix the later for real*complex products --- Eigen/src/Core/DiagonalProduct.h | 6 +++--- test/mixingtypes.cpp | 28 +++++++++++++++++++--------- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/Eigen/src/Core/DiagonalProduct.h b/Eigen/src/Core/DiagonalProduct.h index b838d1b31..7eaa380eb 100644 --- a/Eigen/src/Core/DiagonalProduct.h +++ b/Eigen/src/Core/DiagonalProduct.h @@ -29,7 +29,7 @@ template struct ei_traits > { - typedef typename MatrixType::Scalar Scalar; + typedef typename ei_scalar_product_traits::ReturnType Scalar; enum { RowsAtCompileTime = MatrixType::RowsAtCompileTime, ColsAtCompileTime = MatrixType::ColsAtCompileTime, @@ -62,7 +62,7 @@ class DiagonalProduct : ei_no_assignment_operator, { return m_diagonal.diagonal().coeff(ProductOrder == DiagonalOnTheLeft ? row : col) * m_matrix.coeff(row, col); } - + template EIGEN_STRONG_INLINE PacketScalar packet(int row, int col) const { @@ -72,7 +72,7 @@ class DiagonalProduct : ei_no_assignment_operator, DiagonalVectorPacketLoadMode = (LoadMode == Aligned && ((InnerSize%16) == 0)) ? Aligned : Unaligned }; const int indexInDiagonalVector = ProductOrder == DiagonalOnTheLeft ? row : col; - + if((int(StorageOrder) == RowMajor && int(ProductOrder) == DiagonalOnTheLeft) ||(int(StorageOrder) == ColMajor && int(ProductOrder) == DiagonalOnTheRight)) { diff --git a/test/mixingtypes.cpp b/test/mixingtypes.cpp index 690442a02..6280c3b6e 100644 --- a/test/mixingtypes.cpp +++ b/test/mixingtypes.cpp @@ -33,6 +33,7 @@ #include "main.h" +using namespace std; template void mixingtypes(int size = SizeAtCompileType) { @@ -45,14 +46,14 @@ template void mixingtypes(int size = SizeAtCompileType) typedef Matrix, SizeAtCompileType, 1> Vec_cf; typedef Matrix, SizeAtCompileType, 1> Vec_cd; - Mat_f mf(size,size); - Mat_d md(size,size); - Mat_cf mcf(size,size); - Mat_cd mcd(size,size); - Vec_f vf(size,1); - Vec_d vd(size,1); - Vec_cf vcf(size,1); - Vec_cd vcd(size,1); + Mat_f mf = Mat_f::Random(size,size); + Mat_d md = mf.template cast(); + Mat_cf mcf = Mat_cf::Random(size,size); + Mat_cd mcd = mcf.template cast >(); + Vec_f vf = Vec_f::Random(size,1); + Vec_d vd = vf.template cast(); + Vec_cf vcf = Vec_cf::Random(size,1); + Vec_cd vcd = vcf.template cast >(); mf+mf; VERIFY_RAISES_ASSERT(mf+md); @@ -64,7 +65,16 @@ template void mixingtypes(int size = SizeAtCompileType) vf.dot(vf); VERIFY_RAISES_ASSERT(vd.dot(vf)); VERIFY_RAISES_ASSERT(vcf.dot(vf)); // yeah eventually we should allow this but i'm too lazy to make that change now in Dot.h -} // especially as that might be rewritten as cwise product .sum() which would make that automatic. + // especially as that might be rewritten as cwise product .sum() which would make that automatic. + + VERIFY_IS_APPROX(vf.asDiagonal() * mcf, vf.template cast >().asDiagonal() * mcf); + VERIFY_IS_APPROX(vcd.asDiagonal() * md, vcd.asDiagonal() * md.template cast >()); + VERIFY_IS_APPROX(mcf * vf.asDiagonal(), mcf * vf.template cast >().asDiagonal()); + VERIFY_IS_APPROX(md * vcd.asDiagonal(), md.template cast >() * vcd.asDiagonal()); + +// vd.asDiagonal() * mf; // does not even compile +// vcd.asDiagonal() * mf; // does not even compile +} void mixingtypes_large(int size) -- cgit v1.2.3 From b0aa2520f120f256c00357948149b64661e54783 Mon Sep 17 00:00:00 2001 From: Gael Guennebaud Date: Fri, 4 Sep 2009 11:22:32 +0200 Subject: * add real scalar * complex matrix, real matrix * complex scalar, and complex scalar * real matrix overloads * allows the inner and outer product specialisations to mix real and complex --- Eigen/src/Core/CwiseUnaryOp.h | 14 ++++++++++++-- Eigen/src/Core/MatrixBase.h | 13 ++++++++++--- Eigen/src/Core/Product.h | 28 +++++++++++++++------------- Eigen/src/Core/ProductBase.h | 4 ++-- Eigen/src/Core/util/XprHelper.h | 4 ++++ test/mixingtypes.cpp | 36 +++++++++++++++++++++++++++--------- 6 files changed, 70 insertions(+), 29 deletions(-) diff --git a/Eigen/src/Core/CwiseUnaryOp.h b/Eigen/src/Core/CwiseUnaryOp.h index 6e4c0d4ec..03011800c 100644 --- a/Eigen/src/Core/CwiseUnaryOp.h +++ b/Eigen/src/Core/CwiseUnaryOp.h @@ -232,7 +232,7 @@ Cwise::log() const } -/** \relates MatrixBase */ +/** \returns an expression of \c *this scaled by the scalar factor \a scalar */ template EIGEN_STRONG_INLINE const typename MatrixBase::ScalarMultipleReturnType MatrixBase::operator*(const Scalar& scalar) const @@ -241,7 +241,17 @@ MatrixBase::operator*(const Scalar& scalar) const (derived(), ei_scalar_multiple_op(scalar)); } -/** \relates MatrixBase */ +/** Overloaded for efficient real matrix times complex scalar value */ +template +EIGEN_STRONG_INLINE const CwiseUnaryOp::Scalar, + std::complex::Scalar> >, Derived> +MatrixBase::operator*(const std::complex& scalar) const +{ + return CwiseUnaryOp >, Derived> + (*static_cast(this), ei_scalar_multiple2_op >(scalar)); +} + +/** \returns an expression of \c *this divided by the scalar value \a scalar */ template EIGEN_STRONG_INLINE const CwiseUnaryOp::Scalar>, Derived> MatrixBase::operator/(const Scalar& scalar) const diff --git a/Eigen/src/Core/MatrixBase.h b/Eigen/src/Core/MatrixBase.h index fececdd5f..ad5fde562 100644 --- a/Eigen/src/Core/MatrixBase.h +++ b/Eigen/src/Core/MatrixBase.h @@ -35,7 +35,7 @@ * * Notice that this class is trivial, it is only used to disambiguate overloaded functions. */ -template struct AnyMatrixBase +template struct AnyMatrixBase : public ei_special_scalar_op_base::Scalar, typename NumTraits::Scalar>::Real> { @@ -93,7 +93,7 @@ template struct AnyMatrixBase */ template class MatrixBase #ifndef EIGEN_PARSED_BY_DOXYGEN - : public AnyMatrixBase + : public AnyMatrixBase #endif // not EIGEN_PARSED_BY_DOXYGEN { public: @@ -419,10 +419,17 @@ template class MatrixBase const CwiseUnaryOp::Scalar>, Derived> operator/(const Scalar& scalar) const; - inline friend const CwiseUnaryOp::Scalar>, Derived> + const CwiseUnaryOp >, Derived> + operator*(const std::complex& scalar) const; + + inline friend const ScalarMultipleReturnType operator*(const Scalar& scalar, const MatrixBase& matrix) { return matrix*scalar; } + inline friend const CwiseUnaryOp >, Derived> + operator*(const std::complex& scalar, const MatrixBase& matrix) + { return matrix*scalar; } + template const typename ProductReturnType::Type operator*(const MatrixBase &other) const; diff --git a/Eigen/src/Core/Product.h b/Eigen/src/Core/Product.h index dfdbca839..e7227d4f6 100644 --- a/Eigen/src/Core/Product.h +++ b/Eigen/src/Core/Product.h @@ -84,18 +84,18 @@ public: * based on the three dimensions of the product. * This is a compile time mapping from {1,Small,Large}^3 -> {product types} */ // FIXME I'm not sure the current mapping is the ideal one. -template struct ei_product_type_selector { enum { ret = OuterProduct }; }; -template struct ei_product_type_selector<1,1,Depth> { enum { ret = InnerProduct }; }; -template<> struct ei_product_type_selector<1,1,1> { enum { ret = InnerProduct }; }; -template<> struct ei_product_type_selector { enum { ret = UnrolledProduct }; }; -template<> struct ei_product_type_selector<1,Small,Small> { enum { ret = UnrolledProduct }; }; +template struct ei_product_type_selector { enum { ret = OuterProduct }; }; +template struct ei_product_type_selector<1, 1, Depth> { enum { ret = InnerProduct }; }; +template<> struct ei_product_type_selector<1, 1, 1> { enum { ret = InnerProduct }; }; +template<> struct ei_product_type_selector { enum { ret = UnrolledProduct }; }; +template<> struct ei_product_type_selector<1, Small,Small> { enum { ret = UnrolledProduct }; }; template<> struct ei_product_type_selector { enum { ret = UnrolledProduct }; }; -template<> struct ei_product_type_selector<1,Large,Small> { enum { ret = GemvProduct }; }; -template<> struct ei_product_type_selector<1,Large,Large> { enum { ret = GemvProduct }; }; -template<> struct ei_product_type_selector<1,Small,Large> { enum { ret = GemvProduct }; }; -template<> struct ei_product_type_selector { enum { ret = GemvProduct }; }; -template<> struct ei_product_type_selector { enum { ret = GemvProduct }; }; -template<> struct ei_product_type_selector { enum { ret = GemvProduct }; }; +template<> struct ei_product_type_selector<1, Large,Small> { enum { ret = GemvProduct }; }; +template<> struct ei_product_type_selector<1, Large,Large> { enum { ret = GemvProduct }; }; +template<> struct ei_product_type_selector<1, Small,Large> { enum { ret = GemvProduct }; }; +template<> struct ei_product_type_selector { enum { ret = GemvProduct }; }; +template<> struct ei_product_type_selector { enum { ret = GemvProduct }; }; +template<> struct ei_product_type_selector { enum { ret = GemvProduct }; }; template<> struct ei_product_type_selector { enum { ret = GemmProduct }; }; template<> struct ei_product_type_selector { enum { ret = GemmProduct }; }; template<> struct ei_product_type_selector { enum { ret = GemmProduct }; }; @@ -164,7 +164,7 @@ class GeneralProduct GeneralProduct(const Lhs& lhs, const Rhs& rhs) : Base(lhs,rhs) { - EIGEN_STATIC_ASSERT((ei_is_same_type::ret), + EIGEN_STATIC_ASSERT((ei_is_same_type::ret), YOU_MIXED_DIFFERENT_NUMERIC_TYPES__YOU_NEED_TO_USE_THE_CAST_METHOD_OF_MATRIXBASE_TO_CAST_NUMERIC_TYPES_EXPLICITLY) } @@ -203,7 +203,7 @@ class GeneralProduct GeneralProduct(const Lhs& lhs, const Rhs& rhs) : Base(lhs,rhs) { - EIGEN_STATIC_ASSERT((ei_is_same_type::ret), + EIGEN_STATIC_ASSERT((ei_is_same_type::ret), YOU_MIXED_DIFFERENT_NUMERIC_TYPES__YOU_NEED_TO_USE_THE_CAST_METHOD_OF_MATRIXBASE_TO_CAST_NUMERIC_TYPES_EXPLICITLY) } @@ -217,6 +217,7 @@ template<> struct ei_outer_product_selector { template EIGEN_DONT_INLINE static void run(const ProductType& prod, Dest& dest, typename ProductType::Scalar alpha) { // FIXME make sure lhs is sequentially stored + // FIXME not very good if rhs is real and lhs complex while alpha is real too const int cols = dest.cols(); for (int j=0; j struct ei_outer_product_selector { template EIGEN_DONT_INLINE static void run(const ProductType& prod, Dest& dest, typename ProductType::Scalar alpha) { // FIXME make sure rhs is sequentially stored + // FIXME not very good if lhs is real and rhs complex while alpha is real too const int rows = dest.rows(); for (int i=0; i > { typedef typename ei_cleantype<_Lhs>::type Lhs; typedef typename ei_cleantype<_Rhs>::type Rhs; - typedef typename ei_traits::Scalar Scalar; + typedef typename ei_scalar_product_traits::ReturnType Scalar; enum { RowsAtCompileTime = ei_traits::RowsAtCompileTime, ColsAtCompileTime = ei_traits::ColsAtCompileTime, @@ -146,7 +146,7 @@ class ScaledProduct; // functions of ProductBase, because, otherwise we would have to // define all overloads defined in MatrixBase. Furthermore, Using // "using Base::operator*" would not work with MSVC. -// +// // Also note that here we accept any compatible scalar types template const ScaledProduct diff --git a/Eigen/src/Core/util/XprHelper.h b/Eigen/src/Core/util/XprHelper.h index 871259b08..2f8d35d05 100644 --- a/Eigen/src/Core/util/XprHelper.h +++ b/Eigen/src/Core/util/XprHelper.h @@ -233,6 +233,10 @@ struct ei_special_scalar_op_base return CwiseUnaryOp, Derived> (*static_cast(this), ei_scalar_multiple2_op(scalar)); } + + inline friend const CwiseUnaryOp, Derived> + operator*(const OtherScalar& scalar, const Derived& matrix) + { return matrix*scalar; } }; /** \internal Gives the type of a sub-matrix or sub-vector of a matrix of type \a ExpressionType and size \a Size diff --git a/test/mixingtypes.cpp b/test/mixingtypes.cpp index 6280c3b6e..7dc57e6f7 100644 --- a/test/mixingtypes.cpp +++ b/test/mixingtypes.cpp @@ -54,6 +54,11 @@ template void mixingtypes(int size = SizeAtCompileType) Vec_d vd = vf.template cast(); Vec_cf vcf = Vec_cf::Random(size,1); Vec_cd vcd = vcf.template cast >(); + float sf = ei_random(); + double sd = ei_random(); + complex scf = ei_random >(); + complex scd = ei_random >(); + mf+mf; VERIFY_RAISES_ASSERT(mf+md); @@ -62,18 +67,31 @@ template void mixingtypes(int size = SizeAtCompileType) VERIFY_RAISES_ASSERT(vf+=vd); VERIFY_RAISES_ASSERT(mcd=md); + // check scalar products + VERIFY_IS_APPROX(vcf * sf , vcf * complex(sf)); + VERIFY_IS_APPROX(sd * vcd, complex(sd) * vcd); + VERIFY_IS_APPROX(vf * scf , vf.template cast >() * scf); + VERIFY_IS_APPROX(scd * vd, scd * vd.template cast >()); + + // check dot product vf.dot(vf); VERIFY_RAISES_ASSERT(vd.dot(vf)); VERIFY_RAISES_ASSERT(vcf.dot(vf)); // yeah eventually we should allow this but i'm too lazy to make that change now in Dot.h // especially as that might be rewritten as cwise product .sum() which would make that automatic. + // check diagonal product VERIFY_IS_APPROX(vf.asDiagonal() * mcf, vf.template cast >().asDiagonal() * mcf); VERIFY_IS_APPROX(vcd.asDiagonal() * md, vcd.asDiagonal() * md.template cast >()); VERIFY_IS_APPROX(mcf * vf.asDiagonal(), mcf * vf.template cast >().asDiagonal()); VERIFY_IS_APPROX(md * vcd.asDiagonal(), md.template cast >() * vcd.asDiagonal()); - // vd.asDiagonal() * mf; // does not even compile // vcd.asDiagonal() * mf; // does not even compile + + // check inner product + VERIFY_IS_APPROX((vf.transpose() * vcf).value(), (vf.template cast >().transpose() * vcf).value()); + + // check outer product + VERIFY_IS_APPROX((vf * vcf.transpose()).eval(), (vf.template cast >() * vcf.transpose()).eval()); } @@ -108,9 +126,9 @@ void mixingtypes_large(int size) // VERIFY_RAISES_ASSERT(vcd = md*vcd); // does not even compile (cannot convert complex to double) VERIFY_RAISES_ASSERT(vcf = mcf*vf); - VERIFY_RAISES_ASSERT(mf*md); - VERIFY_RAISES_ASSERT(mcf*mcd); - VERIFY_RAISES_ASSERT(mcf*vcd); +// VERIFY_RAISES_ASSERT(mf*md); // does not even compile +// VERIFY_RAISES_ASSERT(mcf*mcd); // does not even compile +// VERIFY_RAISES_ASSERT(mcf*vcd); // does not even compile VERIFY_RAISES_ASSERT(vcf = mf*vf); } @@ -157,9 +175,9 @@ void test_mixingtypes() { // check that our operator new is indeed called: CALL_SUBTEST(mixingtypes<3>()); - CALL_SUBTEST(mixingtypes<4>()); - CALL_SUBTEST(mixingtypes(20)); - - CALL_SUBTEST(mixingtypes_small<4>()); - CALL_SUBTEST(mixingtypes_large(20)); +// CALL_SUBTEST(mixingtypes<4>()); +// CALL_SUBTEST(mixingtypes(20)); +// +// CALL_SUBTEST(mixingtypes_small<4>()); +// CALL_SUBTEST(mixingtypes_large(20)); } -- cgit v1.2.3 From 80179e954916cdd8222aaa1f5e7b991c421214a5 Mon Sep 17 00:00:00 2001 From: Peter Román Date: Fri, 21 Aug 2009 11:14:45 +0200 Subject: Added support for SuperLU's ILU factorization --- Eigen/src/Sparse/SuperLUSupport.h | 101 ++++++++++++++++++++++++++++++-------- 1 file changed, 81 insertions(+), 20 deletions(-) diff --git a/Eigen/src/Sparse/SuperLUSupport.h b/Eigen/src/Sparse/SuperLUSupport.h index b4f34f094..ad0c5cd92 100644 --- a/Eigen/src/Sparse/SuperLUSupport.h +++ b/Eigen/src/Sparse/SuperLUSupport.h @@ -48,6 +48,29 @@ DECL_GSSVX(SuperLU_C,cgssvx,float,std::complex) DECL_GSSVX(SuperLU_D,dgssvx,double,double) DECL_GSSVX(SuperLU_Z,zgssvx,double,std::complex) +// similarly for the incomplete factorization using gsisx +#define DECL_GSISX(NAMESPACE,FNAME,FLOATTYPE,KEYTYPE) \ + inline float SuperLU_gsisx(superlu_options_t *options, SuperMatrix *A, \ + int *perm_c, int *perm_r, int *etree, char *equed, \ + FLOATTYPE *R, FLOATTYPE *C, SuperMatrix *L, \ + SuperMatrix *U, void *work, int lwork, \ + SuperMatrix *B, SuperMatrix *X, \ + FLOATTYPE *recip_pivot_growth, \ + FLOATTYPE *rcond, \ + SuperLUStat_t *stats, int *info, KEYTYPE) { \ + using namespace NAMESPACE; \ + mem_usage_t mem_usage; \ + NAMESPACE::FNAME(options, A, perm_c, perm_r, etree, equed, R, C, L, \ + U, work, lwork, B, X, recip_pivot_growth, rcond, \ + &mem_usage, stats, info); \ + return mem_usage.for_lu; /* bytes used by the factor storage */ \ + } + +DECL_GSISX(SuperLU_S,sgsisx,float,float) +DECL_GSISX(SuperLU_C,cgsisx,float,std::complex) +DECL_GSISX(SuperLU_D,dgsisx,double,double) +DECL_GSISX(SuperLU_Z,zgsisx,double,std::complex) + template struct SluMatrixMapHelper; @@ -373,7 +396,7 @@ void SparseLU::compute(const MatrixType& a) m_sluA = m_matrix.asSluMatrix(); memset(&m_sluL,0,sizeof m_sluL); memset(&m_sluU,0,sizeof m_sluU); - m_sluEqued = 'B'; + //m_sluEqued = 'B'; int info = 0; m_p.resize(size); @@ -395,14 +418,38 @@ void SparseLU::compute(const MatrixType& a) m_sluX = m_sluB; StatInit(&m_sluStat); - SuperLU_gssvx(&m_sluOptions, &m_sluA, m_q.data(), m_p.data(), &m_sluEtree[0], - &m_sluEqued, &m_sluRscale[0], &m_sluCscale[0], - &m_sluL, &m_sluU, - NULL, 0, - &m_sluB, &m_sluX, - &recip_pivot_gross, &rcond, - &ferr, &berr, - &m_sluStat, &info, Scalar()); + if (m_flags&IncompleteFactorization) + { + ilu_set_default_options(&m_sluOptions); + + // no attempt to preserve column sum + m_sluOptions.ILU_MILU = SILU; + + // only basic ILU(k) support -- no direct control over memory consumption + // better to use ILU_DropRule = DROP_BASIC | DROP_AREA + // and set ILU_FillFactor to max memory growth + m_sluOptions.ILU_DropRule = DROP_BASIC; + m_sluOptions.ILU_DropTol = Base::m_precision; + + SuperLU_gsisx(&m_sluOptions, &m_sluA, m_q.data(), m_p.data(), &m_sluEtree[0], + &m_sluEqued, &m_sluRscale[0], &m_sluCscale[0], + &m_sluL, &m_sluU, + NULL, 0, + &m_sluB, &m_sluX, + &recip_pivot_gross, &rcond, + &m_sluStat, &info, Scalar()); + } + else + { + SuperLU_gssvx(&m_sluOptions, &m_sluA, m_q.data(), m_p.data(), &m_sluEtree[0], + &m_sluEqued, &m_sluRscale[0], &m_sluCscale[0], + &m_sluL, &m_sluU, + NULL, 0, + &m_sluB, &m_sluX, + &recip_pivot_gross, &rcond, + &ferr, &berr, + &m_sluStat, &info, Scalar()); + } StatFree(&m_sluStat); m_extractedDataAreDirty = true; @@ -440,17 +487,31 @@ bool SparseLU::solve(const MatrixBase &b, StatInit(&m_sluStat); int info = 0; RealScalar recip_pivot_gross, rcond; - SuperLU_gssvx( - &m_sluOptions, &m_sluA, - m_q.data(), m_p.data(), - &m_sluEtree[0], &m_sluEqued, - &m_sluRscale[0], &m_sluCscale[0], - &m_sluL, &m_sluU, - NULL, 0, - &m_sluB, &m_sluX, - &recip_pivot_gross, &rcond, - &m_sluFerr[0], &m_sluBerr[0], - &m_sluStat, &info, Scalar()); + + if (m_flags&IncompleteFactorization) + { + SuperLU_gsisx(&m_sluOptions, &m_sluA, m_q.data(), m_p.data(), &m_sluEtree[0], + &m_sluEqued, &m_sluRscale[0], &m_sluCscale[0], + &m_sluL, &m_sluU, + NULL, 0, + &m_sluB, &m_sluX, + &recip_pivot_gross, &rcond, + &m_sluStat, &info, Scalar()); + } + else + { + SuperLU_gssvx( + &m_sluOptions, &m_sluA, + m_q.data(), m_p.data(), + &m_sluEtree[0], &m_sluEqued, + &m_sluRscale[0], &m_sluCscale[0], + &m_sluL, &m_sluU, + NULL, 0, + &m_sluB, &m_sluX, + &recip_pivot_gross, &rcond, + &m_sluFerr[0], &m_sluBerr[0], + &m_sluStat, &info, Scalar()); + } StatFree(&m_sluStat); // reset to previous state -- cgit v1.2.3 From e4f94b8c58bcfe63c444463b69ac272122175d55 Mon Sep 17 00:00:00 2001 From: Gael Guennebaud Date: Fri, 4 Sep 2009 18:19:34 +0200 Subject: enable ILU in super LU only if the super version supports it --- Eigen/src/Sparse/SuperLUSupport.h | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/Eigen/src/Sparse/SuperLUSupport.h b/Eigen/src/Sparse/SuperLUSupport.h index ad0c5cd92..98d598809 100644 --- a/Eigen/src/Sparse/SuperLUSupport.h +++ b/Eigen/src/Sparse/SuperLUSupport.h @@ -48,6 +48,12 @@ DECL_GSSVX(SuperLU_C,cgssvx,float,std::complex) DECL_GSSVX(SuperLU_D,dgssvx,double,double) DECL_GSSVX(SuperLU_Z,zgssvx,double,std::complex) +#ifdef MILU_ALPHA +#define EIGEN_SUPERLU_HAS_ILU +#endif + +#ifdef EIGEN_SUPERLU_HAS_ILU + // similarly for the incomplete factorization using gsisx #define DECL_GSISX(NAMESPACE,FNAME,FLOATTYPE,KEYTYPE) \ inline float SuperLU_gsisx(superlu_options_t *options, SuperMatrix *A, \ @@ -71,6 +77,8 @@ DECL_GSISX(SuperLU_C,cgsisx,float,std::complex) DECL_GSISX(SuperLU_D,dgsisx,double,double) DECL_GSISX(SuperLU_Z,zgsisx,double,std::complex) +#endif + template struct SluMatrixMapHelper; @@ -94,7 +102,7 @@ struct SluMatrix : SuperMatrix Store = &storage; storage = other.storage; } - + SluMatrix& operator=(const SluMatrix& other) { SuperMatrix::operator=(static_cast(other)); @@ -420,6 +428,7 @@ void SparseLU::compute(const MatrixType& a) StatInit(&m_sluStat); if (m_flags&IncompleteFactorization) { + #ifdef EIGEN_SUPERLU_HAS_ILU ilu_set_default_options(&m_sluOptions); // no attempt to preserve column sum @@ -430,7 +439,7 @@ void SparseLU::compute(const MatrixType& a) // and set ILU_FillFactor to max memory growth m_sluOptions.ILU_DropRule = DROP_BASIC; m_sluOptions.ILU_DropTol = Base::m_precision; - + SuperLU_gsisx(&m_sluOptions, &m_sluA, m_q.data(), m_p.data(), &m_sluEtree[0], &m_sluEqued, &m_sluRscale[0], &m_sluCscale[0], &m_sluL, &m_sluU, @@ -438,6 +447,11 @@ void SparseLU::compute(const MatrixType& a) &m_sluB, &m_sluX, &recip_pivot_gross, &rcond, &m_sluStat, &info, Scalar()); + #else + std::cerr << "Incomplete factorization is only available in SuperLU v4\n"; + Base::m_succeeded = false; + return; + #endif } else { @@ -490,6 +504,7 @@ bool SparseLU::solve(const MatrixBase &b, if (m_flags&IncompleteFactorization) { + #ifdef EIGEN_SUPERLU_HAS_ILU SuperLU_gsisx(&m_sluOptions, &m_sluA, m_q.data(), m_p.data(), &m_sluEtree[0], &m_sluEqued, &m_sluRscale[0], &m_sluCscale[0], &m_sluL, &m_sluU, @@ -497,6 +512,10 @@ bool SparseLU::solve(const MatrixBase &b, &m_sluB, &m_sluX, &recip_pivot_gross, &rcond, &m_sluStat, &info, Scalar()); + #else + std::cerr << "Incomplete factorization is only available in SuperLU v4\n"; + return false; + #endif } else { -- cgit v1.2.3 From 5eea8f182405fd36eae8dc345cfdce146d7ca83f Mon Sep 17 00:00:00 2001 From: Jitse Niesen Date: Sat, 5 Sep 2009 19:46:33 +0100 Subject: Typos in tutorial 1. --- doc/C01_QuickStartGuide.dox | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/C01_QuickStartGuide.dox b/doc/C01_QuickStartGuide.dox index d43dbd72d..2943aed80 100644 --- a/doc/C01_QuickStartGuide.dox +++ b/doc/C01_QuickStartGuide.dox @@ -129,7 +129,7 @@ The default constructor leaves coefficients uninitialized. Any dynamic size is s Matrix3f A; // construct 3x3 matrix with uninitialized coefficients A(0,0) = 5; // OK MatrixXf B; // construct 0x0 matrix without allocating anything -A(0,0) = 5; // Error, B is uninitialized, doesn't have any coefficients to address +B(0,0) = 5; // Error, B is uninitialized, doesn't have any coefficients to address \endcode In the above example, B is an uninitialized matrix. What to do with such a matrix? You can call resize() on it, or you can assign another matrix to it. Like this: @@ -261,7 +261,7 @@ v = 6 6 6 \subsection TutorialCasting Casting -In Eigen, any matrices of same size and same scalar type are all naturally compatible. The scalar type can be explicitely casted to another one using the template MatrixBase::cast() function: +In Eigen, any matrices of same size and same scalar type are all naturally compatible. The scalar type can be explicitly casted to another one using the template MatrixBase::cast() function: \code Matrix3d md(1,2,3); Matrix3f mf = md.cast(); @@ -328,7 +328,7 @@ In short, all arithmetic operators can be used right away as in the following ex mat4 -= mat1*1.5 + mat2 * (mat3/4); \endcode which includes two matrix scalar products ("mat1*1.5" and "mat3/4"), a matrix-matrix product ("mat2 * (mat3/4)"), -a matrix addition ("+") and substraction with assignment ("-="). +a matrix addition ("+") and subtraction with assignment ("-="). -- cgit v1.2.3 From 61fe2b6a567ac2df67d30e957342a3138b0d23b0 Mon Sep 17 00:00:00 2001 From: Gael Guennebaud Date: Mon, 7 Sep 2009 10:55:33 +0200 Subject: bug fix in SuperLU support: the meaning of Matrix::stride() changed for vectors --- Eigen/src/Sparse/SuperLUSupport.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Eigen/src/Sparse/SuperLUSupport.h b/Eigen/src/Sparse/SuperLUSupport.h index 98d598809..708f177e8 100644 --- a/Eigen/src/Sparse/SuperLUSupport.h +++ b/Eigen/src/Sparse/SuperLUSupport.h @@ -161,7 +161,7 @@ struct SluMatrix : SuperMatrix res.nrow = mat.rows(); res.ncol = mat.cols(); - res.storage.lda = mat.stride(); + res.storage.lda = MatrixType::IsVectorAtCompileTime ? mat.size() : mat.stride(); res.storage.values = mat.data(); return res; } -- cgit v1.2.3 From 225ec02b06e013fd60dceeb6dc83cd08193719b9 Mon Sep 17 00:00:00 2001 From: Gael Guennebaud Date: Mon, 7 Sep 2009 11:15:38 +0200 Subject: fix another .stride() issue in Cholmod support --- Eigen/src/Sparse/CholmodSupport.h | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/Eigen/src/Sparse/CholmodSupport.h b/Eigen/src/Sparse/CholmodSupport.h index ad59c89af..30a33c3dc 100644 --- a/Eigen/src/Sparse/CholmodSupport.h +++ b/Eigen/src/Sparse/CholmodSupport.h @@ -99,7 +99,7 @@ cholmod_dense ei_cholmod_map_eigen_to_dense(MatrixBase& mat) res.nrow = mat.rows(); res.ncol = mat.cols(); res.nzmax = res.nrow * res.ncol; - res.d = mat.derived().stride(); + res.d = Derived::IsVectorAtCompileTime ? mat.derived().size() : mat.derived().stride(); res.x = mat.derived().data(); res.z = 0; @@ -157,7 +157,7 @@ class SparseLLT : public SparseLLT inline const typename Base::CholMatrixType& matrixL(void) const; template - void solveInPlace(MatrixBase &b) const; + bool solveInPlace(MatrixBase &b) const; void compute(const MatrixType& matrix); @@ -216,7 +216,7 @@ SparseLLT::matrixL() const template template -void SparseLLT::solveInPlace(MatrixBase &b) const +bool SparseLLT::solveInPlace(MatrixBase &b) const { const int size = m_cholmodFactor->n; ei_assert(size==b.rows()); @@ -228,9 +228,16 @@ void SparseLLT::solveInPlace(MatrixBase &b) const // as long as our own triangular sparse solver is not fully optimal, // let's use CHOLMOD's one: cholmod_dense cdb = ei_cholmod_map_eigen_to_dense(b); - cholmod_dense* x = cholmod_solve(CHOLMOD_LDLt, m_cholmodFactor, &cdb, &m_cholmod); + //cholmod_dense* x = cholmod_solve(CHOLMOD_LDLt, m_cholmodFactor, &cdb, &m_cholmod); + cholmod_dense* x = cholmod_solve(CHOLMOD_A, m_cholmodFactor, &cdb, &m_cholmod); + if(!x) + { + std::cerr << "Eigen: cholmod_solve failed\n"; + return false; + } b = Matrix::Map(reinterpret_cast(x->x),b.rows()); cholmod_free_dense(&x, &m_cholmod); + return true; } #endif // EIGEN_CHOLMODSUPPORT_H -- cgit v1.2.3 From a921292381d02d4932f6338aeecf9eff559d3c4b Mon Sep 17 00:00:00 2001 From: Gael Guennebaud Date: Mon, 7 Sep 2009 11:26:20 +0200 Subject: uncomment stuff commented for debugging (sorry for the noise) --- test/mixingtypes.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/mixingtypes.cpp b/test/mixingtypes.cpp index 7dc57e6f7..3e322c7fe 100644 --- a/test/mixingtypes.cpp +++ b/test/mixingtypes.cpp @@ -175,9 +175,9 @@ void test_mixingtypes() { // check that our operator new is indeed called: CALL_SUBTEST(mixingtypes<3>()); -// CALL_SUBTEST(mixingtypes<4>()); -// CALL_SUBTEST(mixingtypes(20)); -// -// CALL_SUBTEST(mixingtypes_small<4>()); -// CALL_SUBTEST(mixingtypes_large(20)); + CALL_SUBTEST(mixingtypes<4>()); + CALL_SUBTEST(mixingtypes(20)); + + CALL_SUBTEST(mixingtypes_small<4>()); + CALL_SUBTEST(mixingtypes_large(20)); } -- cgit v1.2.3 From bdcc0bc1570541c4516b83f1710586e214f6499f Mon Sep 17 00:00:00 2001 From: Gael Guennebaud Date: Mon, 7 Sep 2009 11:37:41 +0200 Subject: fix compilation of swap for ICC --- Eigen/src/Core/Matrix.h | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Eigen/src/Core/Matrix.h b/Eigen/src/Core/Matrix.h index 0975b3b77..2774e0d78 100644 --- a/Eigen/src/Core/Matrix.h +++ b/Eigen/src/Core/Matrix.h @@ -713,13 +713,11 @@ class Matrix m_storage.data()[1] = y; } - template + template friend struct ei_matrix_swap_impl; }; -template::ret, - bool IsDynamicSize = MatrixType::SizeAtCompileTime==Dynamic> +template struct ei_matrix_swap_impl { static inline void run(MatrixType& matrix, MatrixBase& other) @@ -729,7 +727,7 @@ struct ei_matrix_swap_impl }; template -struct ei_matrix_swap_impl +struct ei_matrix_swap_impl { static inline void run(MatrixType& matrix, MatrixBase& other) { @@ -741,7 +739,8 @@ template inline void Matrix<_Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols>::swap(const MatrixBase& other) { - ei_matrix_swap_impl::run(*this, *const_cast*>(&other)); + enum { SwapPointers = ei_is_same_type::ret && Base::SizeAtCompileTime==Dynamic }; + ei_matrix_swap_impl::run(*this, *const_cast*>(&other)); } /** \defgroup matrixtypedefs Global matrix typedefs -- cgit v1.2.3 From b56bb441ddbd3e0a5b58a6642fafc99ff30340cf Mon Sep 17 00:00:00 2001 From: Gael Guennebaud Date: Mon, 7 Sep 2009 12:46:16 +0200 Subject: add a stable_norm unit test --- test/CMakeLists.txt | 1 + test/adjoint.cpp | 9 +----- test/cholesky.cpp | 18 ++++++------ test/stable_norm.cpp | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 91 insertions(+), 17 deletions(-) create mode 100644 test/stable_norm.cpp diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 03fbd48fc..4e279ea47 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -115,6 +115,7 @@ ei_add_test(product_trmv ${EI_OFLAG}) ei_add_test(product_trmm ${EI_OFLAG}) ei_add_test(product_trsm ${EI_OFLAG}) ei_add_test(product_notemporary ${EI_OFLAG}) +ei_add_test(stable_norm) ei_add_test(bandmatrix) ei_add_test(cholesky " " "${GSL_LIBRARIES}") ei_add_test(lu ${EI_OFLAG}) diff --git a/test/adjoint.cpp b/test/adjoint.cpp index 964658c65..bebf47ac3 100644 --- a/test/adjoint.cpp +++ b/test/adjoint.cpp @@ -72,13 +72,6 @@ template void adjoint(const MatrixType& m) if(NumTraits::HasFloatingPoint) VERIFY_IS_APPROX(v1.squaredNorm(), v1.norm() * v1.norm()); VERIFY_IS_MUCH_SMALLER_THAN(ei_abs(vzero.dot(v1)), static_cast(1)); - if(NumTraits::HasFloatingPoint) - { - VERIFY_IS_MUCH_SMALLER_THAN(vzero.norm(), static_cast(1)); - VERIFY_IS_APPROX(v1.norm(), v1.stableNorm()); - VERIFY_IS_APPROX(v1.blueNorm(), v1.stableNorm()); - VERIFY_IS_APPROX(v1.hypotNorm(), v1.stableNorm()); - } // check compatibility of dot and adjoint VERIFY(ei_isApprox(v1.dot(square * v2), (square.adjoint() * v1).dot(v2), largerEps)); @@ -124,7 +117,7 @@ void test_adjoint() } // test a large matrix only once CALL_SUBTEST( adjoint(Matrix()) ); - + { MatrixXcf a(10,10), b(10,10); VERIFY_RAISES_ASSERT(a = a.transpose()); diff --git a/test/cholesky.cpp b/test/cholesky.cpp index df937fd0f..526a9f9d0 100644 --- a/test/cholesky.cpp +++ b/test/cholesky.cpp @@ -82,7 +82,7 @@ template void cholesky(const MatrixType& m) // // test gsl itself ! // VERIFY_IS_APPROX(vecB, _vecB); // VERIFY_IS_APPROX(vecX, _vecX); -// +// // Gsl::free(gMatA); // Gsl::free(gSymm); // Gsl::free(gVecB); @@ -149,16 +149,16 @@ void test_cholesky() { for(int i = 0; i < g_repeat; i++) { CALL_SUBTEST( cholesky(Matrix()) ); -// CALL_SUBTEST( cholesky(MatrixXd(1,1)) ); -// CALL_SUBTEST( cholesky(Matrix2d()) ); -// CALL_SUBTEST( cholesky(Matrix3f()) ); -// CALL_SUBTEST( cholesky(Matrix4d()) ); + CALL_SUBTEST( cholesky(MatrixXd(1,1)) ); + CALL_SUBTEST( cholesky(Matrix2d()) ); + CALL_SUBTEST( cholesky(Matrix3f()) ); + CALL_SUBTEST( cholesky(Matrix4d()) ); CALL_SUBTEST( cholesky(MatrixXd(200,200)) ); CALL_SUBTEST( cholesky(MatrixXcd(100,100)) ); } -// CALL_SUBTEST( cholesky_verify_assert() ); -// CALL_SUBTEST( cholesky_verify_assert() ); -// CALL_SUBTEST( cholesky_verify_assert() ); -// CALL_SUBTEST( cholesky_verify_assert() ); + CALL_SUBTEST( cholesky_verify_assert() ); + CALL_SUBTEST( cholesky_verify_assert() ); + CALL_SUBTEST( cholesky_verify_assert() ); + CALL_SUBTEST( cholesky_verify_assert() ); } diff --git a/test/stable_norm.cpp b/test/stable_norm.cpp new file mode 100644 index 000000000..0326f7bc9 --- /dev/null +++ b/test/stable_norm.cpp @@ -0,0 +1,80 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2009 Gael Guennebaud +// +// 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 . + +#include "main.h" + +template void stable_norm(const MatrixType& m) +{ + /* this test covers the following files: + StableNorm.h + */ + + typedef typename MatrixType::Scalar Scalar; + typedef typename NumTraits::Real RealScalar; + + int rows = m.rows(); + int cols = m.cols(); + + Scalar big = ei_random() * std::numeric_limits::max() * 1e-4; + Scalar small = 1/big; + + MatrixType vzero = MatrixType::Zero(rows, cols), + vrand = MatrixType::Random(rows, cols), + vbig(rows, cols), + vsmall(rows,cols); + + vbig.fill(big); + vsmall.fill(small); + + VERIFY_IS_MUCH_SMALLER_THAN(vzero.norm(), static_cast(1)); + VERIFY_IS_APPROX(vrand.stableNorm(), vrand.norm()); + VERIFY_IS_APPROX(vrand.blueNorm(), vrand.norm()); + VERIFY_IS_APPROX(vrand.hypotNorm(), vrand.norm()); + + RealScalar size = m.size(); + + // test overflow + VERIFY_IS_NOT_APPROX(vbig.norm(), ei_sqrt(size)*big); // here the default norm must fail + VERIFY_IS_APPROX(vbig.stableNorm(), ei_sqrt(size)*big); + VERIFY_IS_APPROX(vbig.blueNorm(), ei_sqrt(size)*big); + VERIFY_IS_APPROX(vbig.hypotNorm(), ei_sqrt(size)*big); + + // test underflow + VERIFY_IS_NOT_APPROX(vsmall.norm(), ei_sqrt(size)*small); // here the default norm must fail + VERIFY_IS_APPROX(vsmall.stableNorm(), ei_sqrt(size)*small); + VERIFY_IS_APPROX(vsmall.blueNorm(), ei_sqrt(size)*small); + VERIFY_IS_APPROX(vsmall.hypotNorm(), ei_sqrt(size)*small); +} + +void test_stable_norm() +{ + for(int i = 0; i < g_repeat; i++) { + CALL_SUBTEST( stable_norm(Matrix()) ); + CALL_SUBTEST( stable_norm(Vector4d()) ); + CALL_SUBTEST( stable_norm(VectorXd(ei_random(10,2000))) ); + CALL_SUBTEST( stable_norm(VectorXf(ei_random(10,2000))) ); + CALL_SUBTEST( stable_norm(VectorXcd(ei_random(10,2000))) ); + } +} + -- cgit v1.2.3 From fb5f546161fd3c4620bad9a8aef71faa7f520d1e Mon Sep 17 00:00:00 2001 From: Gael Guennebaud Date: Mon, 7 Sep 2009 12:53:25 +0200 Subject: improve coverage of unitOrthogonal --- test/geo_orthomethods.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/geo_orthomethods.cpp b/test/geo_orthomethods.cpp index 5e1d5bdb4..540a63b82 100644 --- a/test/geo_orthomethods.cpp +++ b/test/geo_orthomethods.cpp @@ -86,10 +86,10 @@ template void orthomethods(int size=Size) VERIFY_IS_MUCH_SMALLER_THAN(v0.unitOrthogonal().dot(v0), Scalar(1)); VERIFY_IS_APPROX(v0.unitOrthogonal().norm(), RealScalar(1)); - if (size>3) + if (size>=3) { - v0.template start<3>().setZero(); - v0.end(size-3).setRandom(); + v0.template start<2>().setZero(); + v0.end(size-2).setRandom(); VERIFY_IS_MUCH_SMALLER_THAN(v0.unitOrthogonal().dot(v0), Scalar(1)); VERIFY_IS_APPROX(v0.unitOrthogonal().norm(), RealScalar(1)); -- cgit v1.2.3 From ae1d1c8f6ce77c6137bedd3d8c571297524d0d78 Mon Sep 17 00:00:00 2001 From: Gael Guennebaud Date: Mon, 7 Sep 2009 14:04:56 +0200 Subject: improve coverage of matrix-vector product --- test/product_extra.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/test/product_extra.cpp b/test/product_extra.cpp index fcec362a5..3ad99fc7a 100644 --- a/test/product_extra.cpp +++ b/test/product_extra.cpp @@ -104,13 +104,24 @@ template void product_extra(const MatrixType& m) VERIFY_IS_APPROX((-m1.adjoint() * s2) * (s1 * v1.adjoint()), (-m1.adjoint()*s2).eval() * (s1 * v1.adjoint()).eval()); + // test the vector-matrix product with non aligned starts + int i = ei_random(0,m1.rows()-2); + int j = ei_random(0,m1.cols()-2); + int r = ei_random(1,m1.rows()-i); + int c = ei_random(1,m1.cols()-j); + int i2 = ei_random(0,m1.rows()-1); + int j2 = ei_random(0,m1.cols()-1); + + VERIFY_IS_APPROX(m1.col(j2).adjoint() * m1.block(0,j,m1.rows(),c), m1.col(j2).adjoint().eval() * m1.block(0,j,m1.rows(),c).eval()); + VERIFY_IS_APPROX(m1.block(i,0,r,m1.cols()) * m1.row(i2).adjoint(), m1.block(i,0,r,m1.cols()).eval() * m1.row(i2).adjoint().eval()); + } void test_product_extra() { for(int i = 0; i < g_repeat; i++) { - CALL_SUBTEST( product_extra(MatrixXf(ei_random(1,320), ei_random(1,320))) ); + CALL_SUBTEST( product_extra(MatrixXf(ei_random(2,320), ei_random(2,320))) ); CALL_SUBTEST( product_extra(MatrixXcf(ei_random(50,50), ei_random(50,50))) ); - CALL_SUBTEST( product_extra(Matrix,Dynamic,Dynamic,RowMajor>(ei_random(1,50), ei_random(1,50))) ); + CALL_SUBTEST( product_extra(Matrix,Dynamic,Dynamic,RowMajor>(ei_random(2,50), ei_random(2,50))) ); } } -- cgit v1.2.3 From 8f4bf4ed7ffa4f39957118a5b4162613b21f190a Mon Sep 17 00:00:00 2001 From: Gael Guennebaud Date: Mon, 7 Sep 2009 14:05:27 +0200 Subject: add optional compile flags to enable coverage testing --- cmake/EigenTesting.cmake | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/cmake/EigenTesting.cmake b/cmake/EigenTesting.cmake index e3a87f645..1baa1ae4b 100644 --- a/cmake/EigenTesting.cmake +++ b/cmake/EigenTesting.cmake @@ -153,11 +153,17 @@ macro(ei_init_testing) endmacro(ei_init_testing) if(CMAKE_COMPILER_IS_GNUCXX) + option(EIGEN_COVERAGE_TESTING "Enable/disable gcov" OFF) + if(EIGEN_COVERAGE_TESTING) + set(COVERAGE_FLAGS "-fprofile-arcs -ftest-coverage") + else(EIGEN_COVERAGE_TESTING) + set(COVERAGE_FLAGS "") + endif(EIGEN_ENABLE_COVERAGE_TESTING) if(CMAKE_SYSTEM_NAME MATCHES Linux) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g2") - set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -O2 -g2") - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fno-inline-functions") - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -g2") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COVERAGE_FLAGS} -g2") + set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} ${COVERAGE_FLAGS} -O2 -g2") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${COVERAGE_FLAGS} -fno-inline-functions") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${COVERAGE_FLAGS} -O0 -g2") endif(CMAKE_SYSTEM_NAME MATCHES Linux) set(EI_OFLAG "-O2") elseif(MSVC) -- cgit v1.2.3 From 64095b66108d358ebd31ab2cc596421b2bddfff3 Mon Sep 17 00:00:00 2001 From: Hauke Heibel Date: Mon, 7 Sep 2009 17:22:01 +0200 Subject: Changed the AnyMatrixBase / ei_special_scalar_op inheritance order as proposed by Gael. Added conservativeResizeLike as discussed on the mailing list. --- Eigen/src/Core/Matrix.h | 71 ++++++++++++++++++++++++++++------------- Eigen/src/Core/MatrixBase.h | 5 ++- Eigen/src/Core/util/XprHelper.h | 4 +-- test/conservative_resize.cpp | 46 ++++++++++++++++---------- 4 files changed, 82 insertions(+), 44 deletions(-) diff --git a/Eigen/src/Core/Matrix.h b/Eigen/src/Core/Matrix.h index 2774e0d78..6bbd3a019 100644 --- a/Eigen/src/Core/Matrix.h +++ b/Eigen/src/Core/Matrix.h @@ -308,7 +308,7 @@ class Matrix */ template EIGEN_STRONG_INLINE void resizeLike(const MatrixBase& other) - { + { if(RowsAtCompileTime == 1) { ei_assert(other.isVector()); @@ -324,16 +324,14 @@ class Matrix /** Resizes \c *this to a \a rows x \a cols matrix while leaving old values of *this untouched. * - * This method is intended for dynamic-size matrices, although it is legal to call it on any - * matrix as long as fixed dimensions are left unchanged. If you only want to change the number + * This method is intended for dynamic-size matrices. If you only want to change the number * of rows and/or of columns, you can use conservativeResize(NoChange_t, int), * conservativeResize(int, NoChange_t). * * The top-left part of the resized matrix will be the same as the overlapping top-left corner - * of *this. In case values need to be appended to the matrix they will be uninitialized per - * default and set to zero when init_with_zero is set to true. + * of *this. In case values need to be appended to the matrix they will be uninitialized. */ - inline void conservativeResize(int rows, int cols, bool init_with_zero = false) + inline void conservativeResize(int rows, int cols) { // Note: Here is space for improvement. Basically, for conservativeResize(int,int), // neither RowsAtCompileTime or ColsAtCompileTime must be Dynamic. If only one of the @@ -341,23 +339,23 @@ class Matrix // conservativeResize(NoChange_t, int cols). For these methods new static asserts like // EIGEN_STATIC_ASSERT_DYNAMIC_ROWS and EIGEN_STATIC_ASSERT_DYNAMIC_COLS would be good. EIGEN_STATIC_ASSERT_DYNAMIC_SIZE(Matrix) - PlainMatrixType tmp = init_with_zero ? PlainMatrixType::Zero(rows, cols) : PlainMatrixType(rows,cols); + PlainMatrixType tmp(rows, cols); const int common_rows = std::min(rows, this->rows()); const int common_cols = std::min(cols, this->cols()); tmp.block(0,0,common_rows,common_cols) = this->block(0,0,common_rows,common_cols); this->derived().swap(tmp); } - EIGEN_STRONG_INLINE void conservativeResize(int rows, NoChange_t, bool init_with_zero = false) + EIGEN_STRONG_INLINE void conservativeResize(int rows, NoChange_t) { - // Note: see the comment in conservativeResize(int,int,bool) - conservativeResize(rows, cols(), init_with_zero); + // Note: see the comment in conservativeResize(int,int) + conservativeResize(rows, cols()); } - EIGEN_STRONG_INLINE void conservativeResize(NoChange_t, int cols, bool init_with_zero = false) + EIGEN_STRONG_INLINE void conservativeResize(NoChange_t, int cols) { - // Note: see the comment in conservativeResize(int,int,bool) - conservativeResize(rows(), cols, init_with_zero); + // Note: see the comment in conservativeResize(int,int) + conservativeResize(rows(), cols); } /** Resizes \c *this to a vector of length \a size while retaining old values of *this. @@ -366,21 +364,23 @@ class Matrix * partially dynamic matrices when the static dimension is anything other * than 1. For example it will not work with Matrix. * - * When values are appended, they will be uninitialized per default and set - * to zero when init_with_zero is set to true. + * When values are appended, they will be uninitialized. */ - inline void conservativeResize(int size, bool init_with_zero = false) + inline void conservativeResize(int size) { EIGEN_STATIC_ASSERT_VECTOR_ONLY(Matrix) EIGEN_STATIC_ASSERT_DYNAMIC_SIZE(Matrix) - if (RowsAtCompileTime == 1 || ColsAtCompileTime == 1) - { - PlainMatrixType tmp = init_with_zero ? PlainMatrixType::Zero(size) : PlainMatrixType(size); - const int common_size = std::min(this->size(),size); - tmp.segment(0,common_size) = this->segment(0,common_size); - this->derived().swap(tmp); - } + PlainMatrixType tmp(size); + const int common_size = std::min(this->size(),size); + tmp.segment(0,common_size) = this->segment(0,common_size); + this->derived().swap(tmp); + } + + template + inline void conservativeResizeLike(const MatrixBase& other) + { + ei_conservative_resize_like_impl::run(*this, other); } /** Copies the value of the expression \a other into \c *this with automatic resizing. @@ -717,6 +717,31 @@ class Matrix friend struct ei_matrix_swap_impl; }; +template (Derived::IsVectorAtCompileTime)> +struct ei_conservative_resize_like_impl +{ + static void run(MatrixBase& _this, const MatrixBase& other) + { + MatrixBase::PlainMatrixType tmp(other); + const int common_rows = std::min(tmp.rows(), _this.rows()); + const int common_cols = std::min(tmp.cols(), _this.cols()); + tmp.block(0,0,common_rows,common_cols) = _this.block(0,0,common_rows,common_cols); + _this.derived().swap(tmp); + } +}; + +template +struct ei_conservative_resize_like_impl +{ + static void run(MatrixBase& _this, const MatrixBase& other) + { + MatrixBase::PlainMatrixType tmp(other); + const int common_size = std::min(_this.size(),tmp.size()); + tmp.segment(0,common_size) = _this.segment(0,common_size); + _this.derived().swap(tmp); + } +}; + template struct ei_matrix_swap_impl { diff --git a/Eigen/src/Core/MatrixBase.h b/Eigen/src/Core/MatrixBase.h index ad5fde562..25a0545c6 100644 --- a/Eigen/src/Core/MatrixBase.h +++ b/Eigen/src/Core/MatrixBase.h @@ -36,8 +36,6 @@ * Notice that this class is trivial, it is only used to disambiguate overloaded functions. */ template struct AnyMatrixBase - : public ei_special_scalar_op_base::Scalar, - typename NumTraits::Scalar>::Real> { typedef typename ei_plain_matrix_type::type PlainMatrixType; @@ -93,7 +91,8 @@ template struct AnyMatrixBase */ template class MatrixBase #ifndef EIGEN_PARSED_BY_DOXYGEN - : public AnyMatrixBase + : public ei_special_scalar_op_base::Scalar, + typename NumTraits::Scalar>::Real> #endif // not EIGEN_PARSED_BY_DOXYGEN { public: diff --git a/Eigen/src/Core/util/XprHelper.h b/Eigen/src/Core/util/XprHelper.h index 2f8d35d05..cea2faaa8 100644 --- a/Eigen/src/Core/util/XprHelper.h +++ b/Eigen/src/Core/util/XprHelper.h @@ -217,7 +217,7 @@ template struct ei_are_flags_consistent * overloads for complex types */ template::ret > -struct ei_special_scalar_op_base +struct ei_special_scalar_op_base : public AnyMatrixBase { // dummy operator* so that the // "using ei_special_scalar_op_base::operator*" compiles @@ -225,7 +225,7 @@ struct ei_special_scalar_op_base }; template -struct ei_special_scalar_op_base +struct ei_special_scalar_op_base : public AnyMatrixBase { const CwiseUnaryOp, Derived> operator*(const OtherScalar& scalar) const diff --git a/test/conservative_resize.cpp b/test/conservative_resize.cpp index f0d025283..e4ff4f331 100644 --- a/test/conservative_resize.cpp +++ b/test/conservative_resize.cpp @@ -108,22 +108,36 @@ void run_vector_tests() } } +template +void run_resize_like_tests() +{ + typedef Matrix MatrixType; + + MatrixType m; + + m = MatrixType::Random(5); + m.conservativeResizeLike( MatrixType::Ones(2) ); + std::cout << m << std::endl; +} + void test_conservative_resize() { - run_matrix_tests(); - run_matrix_tests(); - run_matrix_tests(); - run_matrix_tests(); - run_matrix_tests(); - run_matrix_tests(); - run_matrix_tests, Eigen::RowMajor>(); - run_matrix_tests, Eigen::ColMajor>(); - run_matrix_tests, Eigen::RowMajor>(); - run_matrix_tests, Eigen::ColMajor>(); - - run_vector_tests(); - run_vector_tests(); - run_vector_tests(); - run_vector_tests >(); - run_vector_tests >(); + run_resize_like_tests(); + + //run_matrix_tests(); + //run_matrix_tests(); + //run_matrix_tests(); + //run_matrix_tests(); + //run_matrix_tests(); + //run_matrix_tests(); + //run_matrix_tests, Eigen::RowMajor>(); + //run_matrix_tests, Eigen::ColMajor>(); + //run_matrix_tests, Eigen::RowMajor>(); + //run_matrix_tests, Eigen::ColMajor>(); + + //run_vector_tests(); + //run_vector_tests(); + //run_vector_tests(); + //run_vector_tests >(); + //run_vector_tests >(); } -- cgit v1.2.3 From e49236bac6ee5689cd3719640dbe785430acbdda Mon Sep 17 00:00:00 2001 From: Hauke Heibel Date: Mon, 7 Sep 2009 17:23:29 +0200 Subject: Ups - that was not intended to be part of the commit. --- test/conservative_resize.cpp | 46 +++++++++++++++----------------------------- 1 file changed, 16 insertions(+), 30 deletions(-) diff --git a/test/conservative_resize.cpp b/test/conservative_resize.cpp index e4ff4f331..f0d025283 100644 --- a/test/conservative_resize.cpp +++ b/test/conservative_resize.cpp @@ -108,36 +108,22 @@ void run_vector_tests() } } -template -void run_resize_like_tests() -{ - typedef Matrix MatrixType; - - MatrixType m; - - m = MatrixType::Random(5); - m.conservativeResizeLike( MatrixType::Ones(2) ); - std::cout << m << std::endl; -} - void test_conservative_resize() { - run_resize_like_tests(); - - //run_matrix_tests(); - //run_matrix_tests(); - //run_matrix_tests(); - //run_matrix_tests(); - //run_matrix_tests(); - //run_matrix_tests(); - //run_matrix_tests, Eigen::RowMajor>(); - //run_matrix_tests, Eigen::ColMajor>(); - //run_matrix_tests, Eigen::RowMajor>(); - //run_matrix_tests, Eigen::ColMajor>(); - - //run_vector_tests(); - //run_vector_tests(); - //run_vector_tests(); - //run_vector_tests >(); - //run_vector_tests >(); + run_matrix_tests(); + run_matrix_tests(); + run_matrix_tests(); + run_matrix_tests(); + run_matrix_tests(); + run_matrix_tests(); + run_matrix_tests, Eigen::RowMajor>(); + run_matrix_tests, Eigen::ColMajor>(); + run_matrix_tests, Eigen::RowMajor>(); + run_matrix_tests, Eigen::ColMajor>(); + + run_vector_tests(); + run_vector_tests(); + run_vector_tests(); + run_vector_tests >(); + run_vector_tests >(); } -- cgit v1.2.3 From 437a79e1ab2606aa0d6346df0bee99347e771e01 Mon Sep 17 00:00:00 2001 From: Hauke Heibel Date: Mon, 7 Sep 2009 17:48:42 +0200 Subject: Fixed unit test and improved code reusage for resizing. --- Eigen/src/Core/Matrix.h | 35 ++++++++++++++--------------------- test/conservative_resize.cpp | 4 ++-- 2 files changed, 16 insertions(+), 23 deletions(-) diff --git a/Eigen/src/Core/Matrix.h b/Eigen/src/Core/Matrix.h index 6bbd3a019..6eae75dcd 100644 --- a/Eigen/src/Core/Matrix.h +++ b/Eigen/src/Core/Matrix.h @@ -331,19 +331,9 @@ class Matrix * The top-left part of the resized matrix will be the same as the overlapping top-left corner * of *this. In case values need to be appended to the matrix they will be uninitialized. */ - inline void conservativeResize(int rows, int cols) + EIGEN_STRONG_INLINE void conservativeResize(int rows, int cols) { - // Note: Here is space for improvement. Basically, for conservativeResize(int,int), - // neither RowsAtCompileTime or ColsAtCompileTime must be Dynamic. If only one of the - // dimensions is dynamic, one could use either conservativeResize(int rows, NoChange_t) or - // conservativeResize(NoChange_t, int cols). For these methods new static asserts like - // EIGEN_STATIC_ASSERT_DYNAMIC_ROWS and EIGEN_STATIC_ASSERT_DYNAMIC_COLS would be good. - EIGEN_STATIC_ASSERT_DYNAMIC_SIZE(Matrix) - PlainMatrixType tmp(rows, cols); - const int common_rows = std::min(rows, this->rows()); - const int common_cols = std::min(cols, this->cols()); - tmp.block(0,0,common_rows,common_cols) = this->block(0,0,common_rows,common_cols); - this->derived().swap(tmp); + conservativeResizeLike(PlainMatrixType(rows, cols)); } EIGEN_STRONG_INLINE void conservativeResize(int rows, NoChange_t) @@ -366,19 +356,13 @@ class Matrix * * When values are appended, they will be uninitialized. */ - inline void conservativeResize(int size) + EIGEN_STRONG_INLINE void conservativeResize(int size) { - EIGEN_STATIC_ASSERT_VECTOR_ONLY(Matrix) - EIGEN_STATIC_ASSERT_DYNAMIC_SIZE(Matrix) - - PlainMatrixType tmp(size); - const int common_size = std::min(this->size(),size); - tmp.segment(0,common_size) = this->segment(0,common_size); - this->derived().swap(tmp); + conservativeResizeLike(PlainMatrixType(size)); } template - inline void conservativeResizeLike(const MatrixBase& other) + EIGEN_STRONG_INLINE void conservativeResizeLike(const MatrixBase& other) { ei_conservative_resize_like_impl::run(*this, other); } @@ -722,6 +706,14 @@ struct ei_conservative_resize_like_impl { static void run(MatrixBase& _this, const MatrixBase& other) { + // Note: Here is space for improvement. Basically, for conservativeResize(int,int), + // neither RowsAtCompileTime or ColsAtCompileTime must be Dynamic. If only one of the + // dimensions is dynamic, one could use either conservativeResize(int rows, NoChange_t) or + // conservativeResize(NoChange_t, int cols). For these methods new static asserts like + // EIGEN_STATIC_ASSERT_DYNAMIC_ROWS and EIGEN_STATIC_ASSERT_DYNAMIC_COLS would be good. + EIGEN_STATIC_ASSERT_DYNAMIC_SIZE(Derived) + EIGEN_STATIC_ASSERT_DYNAMIC_SIZE(OtherDerived) + MatrixBase::PlainMatrixType tmp(other); const int common_rows = std::min(tmp.rows(), _this.rows()); const int common_cols = std::min(tmp.cols(), _this.cols()); @@ -735,6 +727,7 @@ struct ei_conservative_resize_like_impl { static void run(MatrixBase& _this, const MatrixBase& other) { + // segment(...) will check whether Derived/OtherDerived are vectors! MatrixBase::PlainMatrixType tmp(other); const int common_size = std::min(_this.size(),tmp.size()); tmp.segment(0,common_size) = _this.segment(0,common_size); diff --git a/test/conservative_resize.cpp b/test/conservative_resize.cpp index f0d025283..b92dd5449 100644 --- a/test/conservative_resize.cpp +++ b/test/conservative_resize.cpp @@ -65,7 +65,7 @@ void run_matrix_tests() const int rows = ei_random(50,75); const int cols = ei_random(50,75); m = n = MatrixType::Random(50,50); - m.conservativeResize(rows,cols,true); + m.conservativeResizeLike(MatrixType::Zero(rows,cols)); VERIFY_IS_APPROX(m.block(0,0,n.rows(),n.cols()), n); VERIFY( rows<=50 || m.block(50,0,rows-50,cols).sum() == Scalar(0) ); VERIFY( cols<=50 || m.block(0,50,rows,cols-50).sum() == Scalar(0) ); @@ -102,7 +102,7 @@ void run_vector_tests() { const int size = ei_random(50,100); m = n = MatrixType::Random(50); - m.conservativeResize(size,true); + m.conservativeResizeLike(MatrixType::Zero(size)); VERIFY_IS_APPROX(m.segment(0,50), n); VERIFY( size<=50 || m.segment(50,size-50).sum() == Scalar(0) ); } -- cgit v1.2.3 From e6cac85333c9d03421ded5ea356f806521ae58c4 Mon Sep 17 00:00:00 2001 From: Hauke Heibel Date: Tue, 8 Sep 2009 08:27:18 +0200 Subject: Added missing casts. --- test/stable_norm.cpp | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/test/stable_norm.cpp b/test/stable_norm.cpp index 0326f7bc9..b8fbf5271 100644 --- a/test/stable_norm.cpp +++ b/test/stable_norm.cpp @@ -36,8 +36,8 @@ template void stable_norm(const MatrixType& m) int rows = m.rows(); int cols = m.cols(); - Scalar big = ei_random() * std::numeric_limits::max() * 1e-4; - Scalar small = 1/big; + Scalar big = ei_random() * std::numeric_limits::max() * RealScalar(1e-4); + Scalar small = static_cast(1)/big; MatrixType vzero = MatrixType::Zero(rows, cols), vrand = MatrixType::Random(rows, cols), @@ -52,19 +52,19 @@ template void stable_norm(const MatrixType& m) VERIFY_IS_APPROX(vrand.blueNorm(), vrand.norm()); VERIFY_IS_APPROX(vrand.hypotNorm(), vrand.norm()); - RealScalar size = m.size(); + RealScalar size = static_cast(m.size()); // test overflow - VERIFY_IS_NOT_APPROX(vbig.norm(), ei_sqrt(size)*big); // here the default norm must fail - VERIFY_IS_APPROX(vbig.stableNorm(), ei_sqrt(size)*big); - VERIFY_IS_APPROX(vbig.blueNorm(), ei_sqrt(size)*big); - VERIFY_IS_APPROX(vbig.hypotNorm(), ei_sqrt(size)*big); + VERIFY_IS_NOT_APPROX(static_cast(vbig.norm()), ei_sqrt(size)*big); // here the default norm must fail + VERIFY_IS_APPROX(static_cast(vbig.stableNorm()), ei_sqrt(size)*big); + VERIFY_IS_APPROX(static_cast(vbig.blueNorm()), ei_sqrt(size)*big); + VERIFY_IS_APPROX(static_cast(vbig.hypotNorm()), ei_sqrt(size)*big); // test underflow - VERIFY_IS_NOT_APPROX(vsmall.norm(), ei_sqrt(size)*small); // here the default norm must fail - VERIFY_IS_APPROX(vsmall.stableNorm(), ei_sqrt(size)*small); - VERIFY_IS_APPROX(vsmall.blueNorm(), ei_sqrt(size)*small); - VERIFY_IS_APPROX(vsmall.hypotNorm(), ei_sqrt(size)*small); + VERIFY_IS_NOT_APPROX(static_cast(vsmall.norm()), ei_sqrt(size)*small); // here the default norm must fail + VERIFY_IS_APPROX(static_cast(vsmall.stableNorm()), ei_sqrt(size)*small); + VERIFY_IS_APPROX(static_cast(vsmall.blueNorm()), ei_sqrt(size)*small); + VERIFY_IS_APPROX(static_cast(vsmall.hypotNorm()), ei_sqrt(size)*small); } void test_stable_norm() @@ -77,4 +77,3 @@ void test_stable_norm() CALL_SUBTEST( stable_norm(VectorXcd(ei_random(10,2000))) ); } } - -- cgit v1.2.3 From 3a2499fb112a3220d1a4281d3d451581cf0ebc2e Mon Sep 17 00:00:00 2001 From: Hauke Heibel Date: Tue, 8 Sep 2009 10:02:19 +0200 Subject: Fixed conservative_resize compilation errors. --- Eigen/src/Core/Matrix.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Eigen/src/Core/Matrix.h b/Eigen/src/Core/Matrix.h index 6eae75dcd..c08f12491 100644 --- a/Eigen/src/Core/Matrix.h +++ b/Eigen/src/Core/Matrix.h @@ -25,6 +25,7 @@ #ifndef EIGEN_MATRIX_H #define EIGEN_MATRIX_H +template (Derived::IsVectorAtCompileTime)> struct ei_conservative_resize_like_impl; /** \class Matrix * @@ -701,7 +702,7 @@ class Matrix friend struct ei_matrix_swap_impl; }; -template (Derived::IsVectorAtCompileTime)> +template struct ei_conservative_resize_like_impl { static void run(MatrixBase& _this, const MatrixBase& other) @@ -714,7 +715,7 @@ struct ei_conservative_resize_like_impl EIGEN_STATIC_ASSERT_DYNAMIC_SIZE(Derived) EIGEN_STATIC_ASSERT_DYNAMIC_SIZE(OtherDerived) - MatrixBase::PlainMatrixType tmp(other); + typename MatrixBase::PlainMatrixType tmp(other); const int common_rows = std::min(tmp.rows(), _this.rows()); const int common_cols = std::min(tmp.cols(), _this.cols()); tmp.block(0,0,common_rows,common_cols) = _this.block(0,0,common_rows,common_cols); @@ -728,7 +729,7 @@ struct ei_conservative_resize_like_impl static void run(MatrixBase& _this, const MatrixBase& other) { // segment(...) will check whether Derived/OtherDerived are vectors! - MatrixBase::PlainMatrixType tmp(other); + typename MatrixBase::PlainMatrixType tmp(other); const int common_size = std::min(_this.size(),tmp.size()); tmp.segment(0,common_size) = _this.segment(0,common_size); _this.derived().swap(tmp); -- cgit v1.2.3 From 26ed19e4cf0b1ca46f814d6425e9aa90749f1097 Mon Sep 17 00:00:00 2001 From: Hauke Heibel Date: Tue, 8 Sep 2009 10:20:26 +0200 Subject: Fixed if clause. --- cmake/EigenTesting.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/EigenTesting.cmake b/cmake/EigenTesting.cmake index 1baa1ae4b..faa75c6f4 100644 --- a/cmake/EigenTesting.cmake +++ b/cmake/EigenTesting.cmake @@ -158,7 +158,7 @@ if(CMAKE_COMPILER_IS_GNUCXX) set(COVERAGE_FLAGS "-fprofile-arcs -ftest-coverage") else(EIGEN_COVERAGE_TESTING) set(COVERAGE_FLAGS "") - endif(EIGEN_ENABLE_COVERAGE_TESTING) + endif(EIGEN_COVERAGE_TESTING) if(CMAKE_SYSTEM_NAME MATCHES Linux) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COVERAGE_FLAGS} -g2") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} ${COVERAGE_FLAGS} -O2 -g2") -- cgit v1.2.3 From 220ff5413125e323ad454d325c81624549e9409c Mon Sep 17 00:00:00 2001 From: Jitse Niesen Date: Tue, 8 Sep 2009 14:41:54 +0100 Subject: Fix LaTeX error in doxygen comment. --- Eigen/src/Jacobi/Jacobi.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Eigen/src/Jacobi/Jacobi.h b/Eigen/src/Jacobi/Jacobi.h index 3905f4d8f..eeb81c178 100644 --- a/Eigen/src/Jacobi/Jacobi.h +++ b/Eigen/src/Jacobi/Jacobi.h @@ -123,7 +123,7 @@ bool PlanarRotation::makeJacobi(RealScalar x, Scalar y, RealScalar z) } /** Makes \c *this as a Jacobi rotation \c J such that applying \a J on both the right and left sides of the 2x2 selfadjoint matrix - * \f$ B = \left ( \begin{array}{cc} \text{this}_{pp} & \text{this}_{pq} \\ \overline \text{this}_{pq} & \text{this}_{qq} \end{array} \right )\f$ yields + * \f$ B = \left ( \begin{array}{cc} \text{this}_{pp} & \text{this}_{pq} \\ (\text{this}_{pq})^* & \text{this}_{qq} \end{array} \right )\f$ yields * a diagonal matrix \f$ A = J^* B J \f$ * * Example: \include Jacobi_makeJacobi.cpp -- cgit v1.2.3 From 2a6db40f1064c71400bd0be35da440aa82591331 Mon Sep 17 00:00:00 2001 From: Jitse Niesen Date: Tue, 8 Sep 2009 14:51:34 +0100 Subject: Re-factor matrix exponential. Put all routines in a class. I think this is a cleaner design. --- .../Eigen/src/MatrixFunctions/MatrixExponential.h | 465 ++++++++++----------- 1 file changed, 226 insertions(+), 239 deletions(-) diff --git a/unsupported/Eigen/src/MatrixFunctions/MatrixExponential.h b/unsupported/Eigen/src/MatrixFunctions/MatrixExponential.h index bf5b79955..36d13b7eb 100644 --- a/unsupported/Eigen/src/MatrixFunctions/MatrixExponential.h +++ b/unsupported/Eigen/src/MatrixFunctions/MatrixExponential.h @@ -25,6 +25,10 @@ #ifndef EIGEN_MATRIX_EXPONENTIAL #define EIGEN_MATRIX_EXPONENTIAL +#ifdef _MSC_VER + template Scalar log2(Scalar v) { return std::log(v)/std::log(Scalar(2)); } +#endif + /** \brief Compute the matrix exponential. * * \param M matrix whose exponential is to be computed. @@ -61,260 +65,243 @@ template EIGEN_STRONG_INLINE void ei_matrix_exponential(const MatrixBase &M, typename MatrixBase::PlainMatrixType* result); +/** \brief Class for computing the matrix exponential.*/ +template +class MatrixExponential { -/** \internal \brief Internal helper functions for computing the - * matrix exponential. - */ -namespace MatrixExponentialInternal { + public: + + /** \brief Compute the matrix exponential. + * + * \param M matrix whose exponential is to be computed. + * \param result pointer to the matrix in which to store the result. + */ + MatrixExponential(const MatrixType &M, MatrixType *result); -#ifdef _MSC_VER - template Scalar log2(Scalar v) { return std::log(v)/std::log(Scalar(2)); } -#endif + private: + + // Prevent copying + MatrixExponential(const MatrixExponential&); + MatrixExponential& operator=(const MatrixExponential&); + + /** \brief Compute the (3,3)-Padé approximant to the exponential. + * + * After exit, \f$ (V+U)(V-U)^{-1} \f$ is the Padé + * approximant of \f$ \exp(A) \f$ around \f$ A = 0 \f$. + * + * \param A Argument of matrix exponential + */ + void pade3(const MatrixType &A); + + /** \brief Compute the (5,5)-Padé approximant to the exponential. + * + * After exit, \f$ (V+U)(V-U)^{-1} \f$ is the Padé + * approximant of \f$ \exp(A) \f$ around \f$ A = 0 \f$. + * + * \param A Argument of matrix exponential + */ + void pade5(const MatrixType &A); + + /** \brief Compute the (7,7)-Padé approximant to the exponential. + * + * After exit, \f$ (V+U)(V-U)^{-1} \f$ is the Padé + * approximant of \f$ \exp(A) \f$ around \f$ A = 0 \f$. + * + * \param A Argument of matrix exponential + */ + void pade7(const MatrixType &A); + + /** \brief Compute the (9,9)-Padé approximant to the exponential. + * + * After exit, \f$ (V+U)(V-U)^{-1} \f$ is the Padé + * approximant of \f$ \exp(A) \f$ around \f$ A = 0 \f$. + * + * \param A Argument of matrix exponential + */ + void pade9(const MatrixType &A); + + /** \brief Compute the (13,13)-Padé approximant to the exponential. + * + * After exit, \f$ (V+U)(V-U)^{-1} \f$ is the Padé + * approximant of \f$ \exp(A) \f$ around \f$ A = 0 \f$. + * + * \param A Argument of matrix exponential + */ + void pade13(const MatrixType &A); + + /** \brief Compute Padé approximant to the exponential. + * + * Computes \c m_U, \c m_V and \c m_squarings such that + * \f$ (V+U)(V-U)^{-1} \f$ is a Padé of + * \f$ \exp(2^{-\mbox{squarings}}M) \f$ around \f$ M = 0 \f$. The + * degree of the Padé approximant and the value of + * squarings are chosen such that the approximation error is no + * more than the round-off error. + * + * The argument of this function should correspond with the (real + * part of) the entries of \c m_M. It is used to select the + * correct implementation using overloading. + */ + void computeUV(double); + + /** \brief Compute Padé approximant to the exponential. + * + * \sa computeUV(double); + */ + void computeUV(float); - /** \internal \brief Compute the (3,3)-Padé approximant to - * the exponential. - * - * After exit, \f$ (V+U)(V-U)^{-1} \f$ is the Padé - * approximant of \f$ \exp(M) \f$ around \f$ M = 0 \f$. - * - * \param M Argument of matrix exponential - * \param Id Identity matrix of same size as M - * \param tmp Temporary storage, to be provided by the caller - * \param M2 Temporary storage, to be provided by the caller - * \param U Even-degree terms in numerator of Padé approximant - * \param V Odd-degree terms in numerator of Padé approximant - */ - template - EIGEN_STRONG_INLINE void pade3(const MatrixType &M, const MatrixType& Id, MatrixType& tmp, - MatrixType& M2, MatrixType& U, MatrixType& V) - { - typedef typename ei_traits::Scalar Scalar; - const Scalar b[] = {120., 60., 12., 1.}; - M2.noalias() = M * M; - tmp = b[3]*M2 + b[1]*Id; - U.noalias() = M * tmp; - V = b[2]*M2 + b[0]*Id; - } - - /** \internal \brief Compute the (5,5)-Padé approximant to - * the exponential. - * - * After exit, \f$ (V+U)(V-U)^{-1} \f$ is the Padé - * approximant of \f$ \exp(M) \f$ around \f$ M = 0 \f$. - * - * \param M Argument of matrix exponential - * \param Id Identity matrix of same size as M - * \param tmp Temporary storage, to be provided by the caller - * \param M2 Temporary storage, to be provided by the caller - * \param U Even-degree terms in numerator of Padé approximant - * \param V Odd-degree terms in numerator of Padé approximant - */ - template - EIGEN_STRONG_INLINE void pade5(const MatrixType &M, const MatrixType& Id, MatrixType& tmp, - MatrixType& M2, MatrixType& U, MatrixType& V) - { - typedef typename ei_traits::Scalar Scalar; - const Scalar b[] = {30240., 15120., 3360., 420., 30., 1.}; - M2.noalias() = M * M; - MatrixType M4 = M2 * M2; - tmp = b[5]*M4 + b[3]*M2 + b[1]*Id; - U.noalias() = M * tmp; - V = b[4]*M4 + b[2]*M2 + b[0]*Id; - } - - /** \internal \brief Compute the (7,7)-Padé approximant to - * the exponential. - * - * After exit, \f$ (V+U)(V-U)^{-1} \f$ is the Padé - * approximant of \f$ \exp(M) \f$ around \f$ M = 0 \f$. - * - * \param M Argument of matrix exponential - * \param Id Identity matrix of same size as M - * \param tmp Temporary storage, to be provided by the caller - * \param M2 Temporary storage, to be provided by the caller - * \param U Even-degree terms in numerator of Padé approximant - * \param V Odd-degree terms in numerator of Padé approximant - */ - template - EIGEN_STRONG_INLINE void pade7(const MatrixType &M, const MatrixType& Id, MatrixType& tmp, - MatrixType& M2, MatrixType& U, MatrixType& V) - { - typedef typename ei_traits::Scalar Scalar; - const Scalar b[] = {17297280., 8648640., 1995840., 277200., 25200., 1512., 56., 1.}; - M2.noalias() = M * M; - MatrixType M4 = M2 * M2; - MatrixType M6 = M4 * M2; - tmp = b[7]*M6 + b[5]*M4 + b[3]*M2 + b[1]*Id; - U.noalias() = M * tmp; - V = b[6]*M6 + b[4]*M4 + b[2]*M2 + b[0]*Id; - } - - /** \internal \brief Compute the (9,9)-Padé approximant to - * the exponential. - * - * After exit, \f$ (V+U)(V-U)^{-1} \f$ is the Padé - * approximant of \f$ \exp(M) \f$ around \f$ M = 0 \f$. - * - * \param M Argument of matrix exponential - * \param Id Identity matrix of same size as M - * \param tmp Temporary storage, to be provided by the caller - * \param M2 Temporary storage, to be provided by the caller - * \param U Even-degree terms in numerator of Padé approximant - * \param V Odd-degree terms in numerator of Padé approximant - */ - template - EIGEN_STRONG_INLINE void pade9(const MatrixType &M, const MatrixType& Id, MatrixType& tmp, - MatrixType& M2, MatrixType& U, MatrixType& V) - { typedef typename ei_traits::Scalar Scalar; - const Scalar b[] = {17643225600., 8821612800., 2075673600., 302702400., 30270240., + typedef typename NumTraits::Scalar>::Real RealScalar; + + /** \brief Pointer to matrix whose exponential is to be computed. */ + const MatrixType* m_M; + + /** \brief Even-degree terms in numerator of Padé approximant. */ + MatrixType m_U; + + /** \brief Odd-degree terms in numerator of Padé approximant. */ + MatrixType m_V; + + /** \brief Used for temporary storage. */ + MatrixType m_tmp1; + + /** \brief Used for temporary storage. */ + MatrixType m_tmp2; + + /** \brief Identity matrix of the same size as \c m_M. */ + MatrixType m_Id; + + /** \brief Number of squarings required in the last step. */ + int m_squarings; + + /** \brief L1 norm of m_M. */ + float m_l1norm; +}; + +template +MatrixExponential::MatrixExponential(const MatrixType &M, MatrixType *result) : + m_M(&M), + m_U(M.rows(),M.cols()), + m_V(M.rows(),M.cols()), + m_tmp1(M.rows(),M.cols()), + m_tmp2(M.rows(),M.cols()), + m_Id(MatrixType::Identity(M.rows(), M.cols())), + m_squarings(0), + m_l1norm(static_cast(M.cwise().abs().colwise().sum().maxCoeff())) +{ + computeUV(RealScalar()); + m_tmp1 = m_U + m_V; // numerator of Pade approximant + m_tmp2 = -m_U + m_V; // denominator of Pade approximant + m_tmp2.partialLu().solve(m_tmp1, result); + for (int i=0; i +EIGEN_STRONG_INLINE void MatrixExponential::pade3(const MatrixType &A) +{ + const Scalar b[] = {120., 60., 12., 1.}; + m_tmp1.noalias() = A * A; + m_tmp2 = b[3]*m_tmp1 + b[1]*m_Id; + m_U.noalias() = A * m_tmp2; + m_V = b[2]*m_tmp1 + b[0]*m_Id; +} + +template +EIGEN_STRONG_INLINE void MatrixExponential::pade5(const MatrixType &A) +{ + const Scalar b[] = {30240., 15120., 3360., 420., 30., 1.}; + MatrixType A2 = A * A; + m_tmp1.noalias() = A2 * A2; + m_tmp2 = b[5]*m_tmp1 + b[3]*A2 + b[1]*m_Id; + m_U.noalias() = A * m_tmp2; + m_V = b[4]*m_tmp1 + b[2]*A2 + b[0]*m_Id; +} + +template +EIGEN_STRONG_INLINE void MatrixExponential::pade7(const MatrixType &A) +{ + const Scalar b[] = {17297280., 8648640., 1995840., 277200., 25200., 1512., 56., 1.}; + MatrixType A2 = A * A; + MatrixType A4 = A2 * A2; + m_tmp1.noalias() = A4 * A2; + m_tmp2 = b[7]*m_tmp1 + b[5]*A4 + b[3]*A2 + b[1]*m_Id; + m_U.noalias() = A * m_tmp2; + m_V = b[6]*m_tmp1 + b[4]*A4 + b[2]*A2 + b[0]*m_Id; +} + +template +EIGEN_STRONG_INLINE void MatrixExponential::pade9(const MatrixType &A) +{ + const Scalar b[] = {17643225600., 8821612800., 2075673600., 302702400., 30270240., 2162160., 110880., 3960., 90., 1.}; - M2.noalias() = M * M; - MatrixType M4 = M2 * M2; - MatrixType M6 = M4 * M2; - MatrixType M8 = M6 * M2; - tmp = b[9]*M8 + b[7]*M6 + b[5]*M4 + b[3]*M2 + b[1]*Id; - U.noalias() = M * tmp; - V = b[8]*M8 + b[6]*M6 + b[4]*M4 + b[2]*M2 + b[0]*Id; - } - - /** \internal \brief Compute the (13,13)-Padé approximant to - * the exponential. - * - * After exit, \f$ (V+U)(V-U)^{-1} \f$ is the Padé - * approximant of \f$ \exp(M) \f$ around \f$ M = 0 \f$. - * - * \param M Argument of matrix exponential - * \param Id Identity matrix of same size as M - * \param tmp Temporary storage, to be provided by the caller - * \param M2 Temporary storage, to be provided by the caller - * \param U Even-degree terms in numerator of Padé approximant - * \param V Odd-degree terms in numerator of Padé approximant - */ - template - EIGEN_STRONG_INLINE void pade13(const MatrixType &M, const MatrixType& Id, MatrixType& tmp, - MatrixType& M2, MatrixType& U, MatrixType& V) - { - typedef typename ei_traits::Scalar Scalar; - const Scalar b[] = {64764752532480000., 32382376266240000., 7771770303897600., + MatrixType A2 = A * A; + MatrixType A4 = A2 * A2; + MatrixType A6 = A4 * A2; + m_tmp1.noalias() = A6 * A2; + m_tmp2 = b[9]*m_tmp1 + b[7]*A6 + b[5]*A4 + b[3]*A2 + b[1]*m_Id; + m_U.noalias() = A * m_tmp2; + m_V = b[8]*m_tmp1 + b[6]*A6 + b[4]*A4 + b[2]*A2 + b[0]*m_Id; +} + +template +EIGEN_STRONG_INLINE void MatrixExponential::pade13(const MatrixType &A) +{ + const Scalar b[] = {64764752532480000., 32382376266240000., 7771770303897600., 1187353796428800., 129060195264000., 10559470521600., 670442572800., 33522128640., 1323241920., 40840800., 960960., 16380., 182., 1.}; - M2.noalias() = M * M; - MatrixType M4 = M2 * M2; - MatrixType M6 = M4 * M2; - V = b[13]*M6 + b[11]*M4 + b[9]*M2; - tmp.noalias() = M6 * V; - tmp += b[7]*M6 + b[5]*M4 + b[3]*M2 + b[1]*Id; - U.noalias() = M * tmp; - tmp = b[12]*M6 + b[10]*M4 + b[8]*M2; - V.noalias() = M6 * tmp; - V += b[6]*M6 + b[4]*M4 + b[2]*M2 + b[0]*Id; - } - - /** \internal \brief Helper class for computing Padé - * approximants to the exponential. - */ - template ::Scalar>::Real> - struct computeUV_selector - { - /** \internal \brief Compute Padé approximant to the exponential. - * - * Computes \p U, \p V and \p squarings such that \f$ (V+U)(V-U)^{-1} \f$ - * is a Padé of \f$ \exp(2^{-\mbox{squarings}}M) \f$ - * around \f$ M = 0 \f$. The degree of the Padé - * approximant and the value of squarings are chosen such that - * the approximation error is no more than the round-off error. - * - * \param M Argument of matrix exponential - * \param Id Identity matrix of same size as M - * \param tmp1 Temporary storage, to be provided by the caller - * \param tmp2 Temporary storage, to be provided by the caller - * \param U Even-degree terms in numerator of Padé approximant - * \param V Odd-degree terms in numerator of Padé approximant - * \param l1norm L1 norm of M - * \param squarings Pointer to integer containing number of times - * that the result needs to be squared to find the - * matrix exponential - */ - static void run(const MatrixType &M, const MatrixType& Id, MatrixType& tmp1, MatrixType& tmp2, - MatrixType& U, MatrixType& V, float l1norm, int* squarings); - }; - - template - struct computeUV_selector - { - static void run(const MatrixType &M, const MatrixType& Id, MatrixType& tmp1, MatrixType& tmp2, - MatrixType& U, MatrixType& V, float l1norm, int* squarings) - { - *squarings = 0; - if (l1norm < 4.258730016922831e-001) { - pade3(M, Id, tmp1, tmp2, U, V); - } else if (l1norm < 1.880152677804762e+000) { - pade5(M, Id, tmp1, tmp2, U, V); - } else { - const float maxnorm = 3.925724783138660f; - *squarings = std::max(0, (int)ceil(log2(l1norm / maxnorm))); - MatrixType A = M / std::pow(typename ei_traits::Scalar(2), *squarings); - pade7(A, Id, tmp1, tmp2, U, V); - } - } - }; - - template - struct computeUV_selector - { - static void run(const MatrixType &M, const MatrixType& Id, MatrixType& tmp1, MatrixType& tmp2, - MatrixType& U, MatrixType& V, float l1norm, int* squarings) - { - *squarings = 0; - if (l1norm < 1.495585217958292e-002) { - pade3(M, Id, tmp1, tmp2, U, V); - } else if (l1norm < 2.539398330063230e-001) { - pade5(M, Id, tmp1, tmp2, U, V); - } else if (l1norm < 9.504178996162932e-001) { - pade7(M, Id, tmp1, tmp2, U, V); - } else if (l1norm < 2.097847961257068e+000) { - pade9(M, Id, tmp1, tmp2, U, V); - } else { - const double maxnorm = 5.371920351148152; - *squarings = std::max(0, (int)ceil(log2(l1norm / maxnorm))); - MatrixType A = M / std::pow(typename ei_traits::Scalar(2), *squarings); - pade13(A, Id, tmp1, tmp2, U, V); - } - } - }; - - /** \internal \brief Compute the matrix exponential. - * - * \param M matrix whose exponential is to be computed. - * \param result pointer to the matrix in which to store the result. - */ - template - void compute(const MatrixType &M, MatrixType* result) - { - MatrixType num(M.rows(), M.cols()); - MatrixType den(M.rows(), M.cols()); - MatrixType U(M.rows(), M.cols()); - MatrixType V(M.rows(), M.cols()); - MatrixType Id = MatrixType::Identity(M.rows(), M.cols()); - float l1norm = static_cast(M.cwise().abs().colwise().sum().maxCoeff()); - int squarings; - computeUV_selector::run(M, Id, num, den, U, V, l1norm, &squarings); - num = U + V; // numerator of Pade approximant - den = -U + V; // denominator of Pade approximant - den.partialLu().solve(num, result); - for (int i=0; i +void MatrixExponential::computeUV(float) +{ + if (m_l1norm < 4.258730016922831e-001) { + pade3(*m_M); + } else if (m_l1norm < 1.880152677804762e+000) { + pade5(*m_M); + } else { + const float maxnorm = 3.925724783138660f; + m_squarings = std::max(0, (int)ceil(log2(m_l1norm / maxnorm))); + MatrixType A = *m_M / std::pow(Scalar(2), m_squarings); + pade7(A); } +} -} // end of namespace MatrixExponentialInternal +template +void MatrixExponential::computeUV(double) +{ + if (m_l1norm < 1.495585217958292e-002) { + pade3(*m_M); + } else if (m_l1norm < 2.539398330063230e-001) { + pade5(*m_M); + } else if (m_l1norm < 9.504178996162932e-001) { + pade7(*m_M); + } else if (m_l1norm < 2.097847961257068e+000) { + pade9(*m_M); + } else { + const double maxnorm = 5.371920351148152; + m_squarings = std::max(0, (int)ceil(log2(m_l1norm / maxnorm))); + MatrixType A = *m_M / std::pow(Scalar(2), m_squarings); + pade13(A); + } +} template EIGEN_STRONG_INLINE void ei_matrix_exponential(const MatrixBase &M, typename MatrixBase::PlainMatrixType* result) { ei_assert(M.rows() == M.cols()); - MatrixExponentialInternal::compute(M.eval(), result); + MatrixExponential::PlainMatrixType>(M, result); } #endif // EIGEN_MATRIX_EXPONENTIAL -- cgit v1.2.3
@@ -464,7 +464,7 @@ mat = 2 7 8 Also note that maxCoeff and minCoeff can takes optional arguments returning the coordinates of the respective min/max coeff: \link MatrixBase::maxCoeff(int*,int*) const maxCoeff(int* i, int* j) \endlink, \link MatrixBase::minCoeff(int*,int*) const minCoeff(int* i, int* j) \endlink. -\b Side \b note: The all() and any() functions are especially useful in combinaison with coeff-wise comparison operators (\ref CwiseAll "example"). +\b Side \b note: The all() and any() functions are especially useful in combination with coeff-wise comparison operators (\ref CwiseAll "example"). @@ -578,7 +578,7 @@ vec1.normalize();\endcode top\section TutorialCoreTriangularMatrix Dealing with triangular matrices -Currently, Eigen does not provide any explcit triangular matrix, with storage class. Instead, we +Currently, Eigen does not provide any explicit triangular matrix, with storage class. Instead, we can reference a triangular part of a square matrix or expression to perform special treatment on it. This is achieved by the class TriangularView and the MatrixBase::triangularView template function. Note that the opposite triangular part of the matrix is never referenced, and so it can, e.g., store @@ -595,12 +595,12 @@ m.triangularView() m.triangularView()\endcode
-Writting to a specific triangular part:\n (only the referenced triangular part is evaluated) +Writing to a specific triangular part:\n (only the referenced triangular part is evaluated) \code m1.triangularView() = m2 + m3 \endcode
-Convertion to a dense matrix setting the opposite triangular part to zero: +Conversion to a dense matrix setting the opposite triangular part to zero: \code m2 = m1.triangularView()\endcode