// This file is part of Eigen, a lightweight C++ template library // for linear algebra. // // Copyright (C) 2011 Jitse Niesen // // 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_MATRIX_LOGARITHM #define EIGEN_MATRIX_LOGARITHM #ifndef M_PI #define M_PI 3.14159265358979323846264338327950L #endif /** \ingroup MatrixFunctions_Module * \class MatrixLogarithmAtomic * \brief Helper class for computing matrix logarithm of atomic matrices. * * \internal * Here, an atomic matrix is a triangular matrix whose diagonal * entries are close to each other. * * \sa class MatrixFunctionAtomic, MatrixBase::log() */ template class MatrixLogarithmAtomic { public: typedef typename MatrixType::Scalar Scalar; // typedef typename MatrixType::Index Index; typedef typename NumTraits::Real RealScalar; // typedef typename internal::stem_function::type StemFunction; // typedef Matrix VectorType; /** \brief Constructor. */ MatrixLogarithmAtomic() { } /** \brief Compute matrix logarithm of atomic matrix * \param[in] A argument of matrix logarithm, should be upper triangular and atomic * \returns The logarithm of \p A. */ MatrixType compute(const MatrixType& A); private: void compute2x2(const MatrixType& A, MatrixType& result); void computeBig(const MatrixType& A, MatrixType& result); static Scalar atanh(Scalar x); int getPadeDegree(typename MatrixType::RealScalar normTminusI); void computePade(MatrixType& result, const MatrixType& T, int degree); void computePade3(MatrixType& result, const MatrixType& T); void computePade4(MatrixType& result, const MatrixType& T); void computePade5(MatrixType& result, const MatrixType& T); void computePade6(MatrixType& result, const MatrixType& T); void computePade7(MatrixType& result, const MatrixType& T); static const double maxNormForPade[]; static const int minPadeDegree = 3; static const int maxPadeDegree = 7; // Prevent copying MatrixLogarithmAtomic(const MatrixLogarithmAtomic&); MatrixLogarithmAtomic& operator=(const MatrixLogarithmAtomic&); }; template const double MatrixLogarithmAtomic::maxNormForPade[] = { 0.0162 /* degree = 3 */, 0.0539, 0.114, 0.187, 0.264 }; /** \brief Compute logarithm of triangular matrix with clustered eigenvalues. */ template MatrixType MatrixLogarithmAtomic::compute(const MatrixType& A) { using std::log; MatrixType result(A.rows(), A.rows()); if (A.rows() == 1) result(0,0) = log(A(0,0)); else if (A.rows() == 2) compute2x2(A, result); else computeBig(A, result); return result; } /** \brief Compute atanh (inverse hyperbolic tangent). */ template typename MatrixType::Scalar MatrixLogarithmAtomic::atanh(typename MatrixType::Scalar x) { using std::abs; using std::sqrt; if (abs(x) > sqrt(NumTraits::epsilon())) return Scalar(0.5) * log((Scalar(1) + x) / (Scalar(1) - x)); else return x + x*x*x / Scalar(3); } /** \brief Compute logarithm of 2x2 triangular matrix. */ template void MatrixLogarithmAtomic::compute2x2(const MatrixType& A, MatrixType& result) { using std::abs; using std::ceil; using std::imag; using std::log; Scalar logA00 = log(A(0,0)); Scalar logA11 = log(A(1,1)); result(0,0) = logA00; result(1,0) = Scalar(0); result(1,1) = logA11; if (A(0,0) == A(1,1)) { result(0,1) = A(0,1) / A(0,0); } else if ((abs(A(0,0)) < 0.5*abs(A(1,1))) || (abs(A(0,0)) > 2*abs(A(1,1)))) { result(0,1) = A(0,1) * (logA11 - logA00) / (A(1,1) - A(0,0)); } else { // computation in previous branch is inaccurate if A(1,1) \approx A(0,0) int unwindingNumber = ceil((imag(logA11 - logA00) - M_PI) / (2*M_PI)); Scalar z = (A(1,1) - A(0,0)) / (A(1,1) + A(0,0)); result(0,1) = A(0,1) * (Scalar(2) * atanh(z) + Scalar(0,2*M_PI*unwindingNumber)) / (A(1,1) - A(0,0)); } } /** \brief Compute logarithm of triangular matrices with size > 2. * \details This uses a inverse scale-and-square algorithm. */ template void MatrixLogarithmAtomic::computeBig(const MatrixType& A, MatrixType& result) { int numberOfSquareRoots = 0; int numberOfExtraSquareRoots = 0; int degree; MatrixType T = A; while (true) { RealScalar normTminusI = (T - MatrixType::Identity(T.rows(), T.rows())).cwiseAbs().colwise().sum().maxCoeff(); if (normTminusI < maxNormForPade[maxPadeDegree - minPadeDegree]) { degree = getPadeDegree(normTminusI); int degree2 = getPadeDegree(normTminusI / RealScalar(2)); if ((degree - degree2 <= 1) || (numberOfExtraSquareRoots == 1)) break; ++numberOfExtraSquareRoots; } MatrixType sqrtT; MatrixSquareRootTriangular(T).compute(sqrtT); T = sqrtT; ++numberOfSquareRoots; } computePade(result, T, degree); result *= pow(RealScalar(2), numberOfSquareRoots); } /* \brief Get suitable degree for Pade approximation. */ template int MatrixLogarithmAtomic::getPadeDegree(typename MatrixType::RealScalar normTminusI) { for (int degree = 3; degree <= maxPadeDegree; ++degree) if (normTminusI <= maxNormForPade[degree - minPadeDegree]) return degree; assert(false); // this line should never be reached } /* \brief Compute Pade approximation to matrix logarithm */ template void MatrixLogarithmAtomic::computePade(MatrixType& result, const MatrixType& T, int degree) { switch (degree) { case 3: computePade3(result, T); break; case 4: computePade4(result, T); break; case 5: computePade5(result, T); break; case 6: computePade6(result, T); break; case 7: computePade7(result, T); break; default: assert(false); // should never happen } } template void MatrixLogarithmAtomic::computePade3(MatrixType& result, const MatrixType& T) { const int degree = 3; double nodes[] = { 0.112701665379258, 0.500000000000000, 0.887298334620742 }; double weights[] = { 0.277777777777778, 0.444444444444444, 0.277777777777778 }; MatrixType TminusI = T - MatrixType::Identity(T.rows(), T.rows()); result.setZero(T.rows(), T.rows()); for (int k = 0; k < degree; ++k) result += weights[k] * (MatrixType::Identity(T.rows(), T.rows()) + nodes[k] * TminusI) .template triangularView().solve(TminusI); } template void MatrixLogarithmAtomic::computePade4(MatrixType& result, const MatrixType& T) { const int degree = 4; double nodes[] = { 0.069431844202974, 0.330009478207572, 0.669990521792428, 0.930568155797026 }; double weights[] = { 0.173927422568727, 0.326072577431273, 0.326072577431273, 0.173927422568727 }; MatrixType TminusI = T - MatrixType::Identity(T.rows(), T.rows()); result.setZero(T.rows(), T.rows()); for (int k = 0; k < degree; ++k) result += weights[k] * (MatrixType::Identity(T.rows(), T.rows()) + nodes[k] * TminusI) .template triangularView().solve(TminusI); } template void MatrixLogarithmAtomic::computePade5(MatrixType& result, const MatrixType& T) { const int degree = 5; double nodes[] = { 0.046910077030668, 0.230765344947158, 0.500000000000000, 0.769234655052841, 0.953089922969332 }; double weights[] = { 0.118463442528095, 0.239314335249683, 0.284444444444444, 0.239314335249683, 0.118463442528094 }; MatrixType TminusI = T - MatrixType::Identity(T.rows(), T.rows()); result.setZero(T.rows(), T.rows()); for (int k = 0; k < degree; ++k) result += weights[k] * (MatrixType::Identity(T.rows(), T.rows()) + nodes[k] * TminusI) .template triangularView().solve(TminusI); } template void MatrixLogarithmAtomic::computePade6(MatrixType& result, const MatrixType& T) { const int degree = 6; double nodes[] = { 0.033765242898424, 0.169395306766868, 0.380690406958402, 0.619309593041598, 0.830604693233132, 0.966234757101576 }; double weights[] = { 0.085662246189585, 0.180380786524069, 0.233956967286345, 0.233956967286346, 0.180380786524069, 0.085662246189585 }; MatrixType TminusI = T - MatrixType::Identity(T.rows(), T.rows()); result.setZero(T.rows(), T.rows()); for (int k = 0; k < degree; ++k) result += weights[k] * (MatrixType::Identity(T.rows(), T.rows()) + nodes[k] * TminusI) .template triangularView().solve(TminusI); } template void MatrixLogarithmAtomic::computePade7(MatrixType& result, const MatrixType& T) { const int degree = 7; double nodes[] = { 0.025446043828621, 0.129234407200303, 0.297077424311301, 0.500000000000000, 0.702922575688699, 0.870765592799697, 0.974553956171379 }; double weights[] = { 0.064742483084435, 0.139852695744638, 0.190915025252559, 0.208979591836734, 0.190915025252560, 0.139852695744638, 0.064742483084435 }; MatrixType TminusI = T - MatrixType::Identity(T.rows(), T.rows()); result.setZero(T.rows(), T.rows()); for (int k = 0; k < degree; ++k) result += weights[k] * (MatrixType::Identity(T.rows(), T.rows()) + nodes[k] * TminusI) .template triangularView().solve(TminusI); } /** \ingroup MatrixFunctions_Module * * \brief Proxy for the matrix logarithm of some matrix (expression). * * \tparam Derived Type of the argument to the matrix function. * * This class holds the argument to the matrix function until it is * assigned or evaluated for some other reason (so the argument * should not be changed in the meantime). It is the return type of * matrixBase::matrixLogarithm() and most of the time this is the * only way it is used. */ template class MatrixLogarithmReturnValue : public ReturnByValue > { public: typedef typename Derived::Scalar Scalar; typedef typename Derived::Index Index; /** \brief Constructor. * * \param[in] A %Matrix (expression) forming the argument of the matrix logarithm. */ MatrixLogarithmReturnValue(const Derived& A) : m_A(A) { } /** \brief Compute the matrix logarithm. * * \param[out] result Logarithm of \p A, where \A is as specified in the constructor. */ template inline void evalTo(ResultType& result) const { typedef typename Derived::PlainObject PlainObject; typedef internal::traits Traits; static const int RowsAtCompileTime = Traits::RowsAtCompileTime; static const int ColsAtCompileTime = Traits::ColsAtCompileTime; static const int Options = PlainObject::Options; typedef std::complex::Real> ComplexScalar; typedef Matrix DynMatrixType; typedef MatrixLogarithmAtomic AtomicType; AtomicType atomic; const PlainObject Aevaluated = m_A.eval(); MatrixFunction mf(Aevaluated, atomic); mf.compute(result); } Index rows() const { return m_A.rows(); } Index cols() const { return m_A.cols(); } private: typename internal::nested::type m_A; MatrixLogarithmReturnValue& operator=(const MatrixLogarithmReturnValue&); }; namespace internal { template struct traits > { typedef typename Derived::PlainObject ReturnType; }; } /********** MatrixBase method **********/ template const MatrixLogarithmReturnValue MatrixBase::log() const { eigen_assert(rows() == cols()); return MatrixLogarithmReturnValue(derived()); } #endif // EIGEN_MATRIX_LOGARITHM