#ifndef EIGEN_MATRIX_FUNCTION_H
#define EIGEN_MATRIX_FUNCTION_H
#include "StemFunction.h"
#include "./InternalHeaderCheck.h"
namespace Eigen {
namespace internal {
static const float matrix_function_separation = 0.1f;
template <typename MatrixType>
class MatrixFunctionAtomic {
public:
typedef typename MatrixType::Scalar Scalar;
typedef typename stem_function<Scalar>::type StemFunction;
MatrixFunctionAtomic(StemFunction f) : m_f(f) {}
MatrixType compute(const MatrixType& A);
private:
StemFunction* m_f;
};
template <typename MatrixType>
typename NumTraits<typename MatrixType::Scalar>::Real matrix_function_compute_mu(const MatrixType& A) {
typedef typename plain_col_type<MatrixType>::type VectorType;
Index rows = A.rows();
const MatrixType N = MatrixType::Identity(rows, rows) - A;
VectorType e = VectorType::Ones(rows);
N.template triangularView<Upper>().solveInPlace(e);
return e.cwiseAbs().maxCoeff();
}
template <typename MatrixType>
MatrixType MatrixFunctionAtomic<MatrixType>::compute(const MatrixType& A) {
typedef typename NumTraits<Scalar>::Real RealScalar;
Index rows = A.rows();
Scalar avgEival = A.trace() / Scalar(RealScalar(rows));
MatrixType Ashifted = A - avgEival * MatrixType::Identity(rows, rows);
RealScalar mu = matrix_function_compute_mu(Ashifted);
MatrixType F = m_f(avgEival, 0) * MatrixType::Identity(rows, rows);
MatrixType P = Ashifted;
MatrixType Fincr;
for (Index s = 1; double(s) < 1.1 * double(rows) + 10.0; s++) { Fincr = m_f(avgEival, static_cast<int>(s)) * P;
F += Fincr;
P = Scalar(RealScalar(1) / RealScalar(s + 1)) * P * Ashifted;
const RealScalar F_norm = F.cwiseAbs().rowwise().sum().maxCoeff();
const RealScalar Fincr_norm = Fincr.cwiseAbs().rowwise().sum().maxCoeff();
if (Fincr_norm < NumTraits<Scalar>::epsilon() * F_norm) {
RealScalar delta = 0;
RealScalar rfactorial = 1;
for (Index r = 0; r < rows; r++) {
RealScalar mx = 0;
for (Index i = 0; i < rows; i++)
mx = (std::max)(mx, std::abs(m_f(Ashifted(i, i) + avgEival, static_cast<int>(s + r))));
if (r != 0) rfactorial *= RealScalar(r);
delta = (std::max)(delta, mx / rfactorial);
}
const RealScalar P_norm = P.cwiseAbs().rowwise().sum().maxCoeff();
if (mu * delta * P_norm < NumTraits<Scalar>::epsilon() * F_norm) break;
}
}
return F;
}
template <typename Index, typename ListOfClusters>
typename ListOfClusters::iterator matrix_function_find_cluster(Index key, ListOfClusters& clusters) {
typename std::list<Index>::iterator j;
for (typename ListOfClusters::iterator i = clusters.begin(); i != clusters.end(); ++i) {
j = std::find(i->begin(), i->end(), key);
if (j != i->end()) return i;
}
return clusters.end();
}
template <typename EivalsType, typename Cluster>
void matrix_function_partition_eigenvalues(const EivalsType& eivals, std::list<Cluster>& clusters) {
typedef typename EivalsType::RealScalar RealScalar;
for (Index i = 0; i < eivals.rows(); ++i) {
typename std::list<Cluster>::iterator qi = matrix_function_find_cluster(i, clusters);
if (qi == clusters.end()) {
Cluster l;
l.push_back(i);
clusters.push_back(l);
qi = clusters.end();
--qi;
}
for (Index j = i + 1; j < eivals.rows(); ++j) {
if (abs(eivals(j) - eivals(i)) <= RealScalar(matrix_function_separation) &&
std::find(qi->begin(), qi->end(), j) == qi->end()) {
typename std::list<Cluster>::iterator qj = matrix_function_find_cluster(j, clusters);
if (qj == clusters.end()) {
qi->push_back(j);
} else {
qi->insert(qi->end(), qj->begin(), qj->end());
clusters.erase(qj);
}
}
}
}
}
template <typename ListOfClusters, typename Index>
void matrix_function_compute_cluster_size(const ListOfClusters& clusters, Matrix<Index, Dynamic, 1>& clusterSize) {
const Index numClusters = static_cast<Index>(clusters.size());
clusterSize.setZero(numClusters);
Index clusterIndex = 0;
for (typename ListOfClusters::const_iterator cluster = clusters.begin(); cluster != clusters.end(); ++cluster) {
clusterSize[clusterIndex] = cluster->size();
++clusterIndex;
}
}
template <typename VectorType>
void matrix_function_compute_block_start(const VectorType& clusterSize, VectorType& blockStart) {
blockStart.resize(clusterSize.rows());
blockStart(0) = 0;
for (Index i = 1; i < clusterSize.rows(); i++) {
blockStart(i) = blockStart(i - 1) + clusterSize(i - 1);
}
}
template <typename EivalsType, typename ListOfClusters, typename VectorType>
void matrix_function_compute_map(const EivalsType& eivals, const ListOfClusters& clusters, VectorType& eivalToCluster) {
eivalToCluster.resize(eivals.rows());
Index clusterIndex = 0;
for (typename ListOfClusters::const_iterator cluster = clusters.begin(); cluster != clusters.end(); ++cluster) {
for (Index i = 0; i < eivals.rows(); ++i) {
if (std::find(cluster->begin(), cluster->end(), i) != cluster->end()) {
eivalToCluster[i] = clusterIndex;
}
}
++clusterIndex;
}
}
template <typename DynVectorType, typename VectorType>
void matrix_function_compute_permutation(const DynVectorType& blockStart, const DynVectorType& eivalToCluster,
VectorType& permutation) {
DynVectorType indexNextEntry = blockStart;
permutation.resize(eivalToCluster.rows());
for (Index i = 0; i < eivalToCluster.rows(); i++) {
Index cluster = eivalToCluster[i];
permutation[i] = indexNextEntry[cluster];
++indexNextEntry[cluster];
}
}
template <typename VectorType, typename MatrixType>
void matrix_function_permute_schur(VectorType& permutation, MatrixType& U, MatrixType& T) {
for (Index i = 0; i < permutation.rows() - 1; i++) {
Index j;
for (j = i; j < permutation.rows(); j++) {
if (permutation(j) == i) break;
}
eigen_assert(permutation(j) == i);
for (Index k = j - 1; k >= i; k--) {
JacobiRotation<typename MatrixType::Scalar> rotation;
rotation.makeGivens(T(k, k + 1), T(k + 1, k + 1) - T(k, k));
T.applyOnTheLeft(k, k + 1, rotation.adjoint());
T.applyOnTheRight(k, k + 1, rotation);
U.applyOnTheRight(k, k + 1, rotation);
std::swap(permutation.coeffRef(k), permutation.coeffRef(k + 1));
}
}
}
template <typename MatrixType, typename AtomicType, typename VectorType>
void matrix_function_compute_block_atomic(const MatrixType& T, AtomicType& atomic, const VectorType& blockStart,
const VectorType& clusterSize, MatrixType& fT) {
fT.setZero(T.rows(), T.cols());
for (Index i = 0; i < clusterSize.rows(); ++i) {
fT.block(blockStart(i), blockStart(i), clusterSize(i), clusterSize(i)) =
atomic.compute(T.block(blockStart(i), blockStart(i), clusterSize(i), clusterSize(i)));
}
}
template <typename MatrixType>
MatrixType matrix_function_solve_triangular_sylvester(const MatrixType& A, const MatrixType& B, const MatrixType& C) {
eigen_assert(A.rows() == A.cols());
eigen_assert(A.isUpperTriangular());
eigen_assert(B.rows() == B.cols());
eigen_assert(B.isUpperTriangular());
eigen_assert(C.rows() == A.rows());
eigen_assert(C.cols() == B.rows());
typedef typename MatrixType::Scalar Scalar;
Index m = A.rows();
Index n = B.rows();
MatrixType X(m, n);
for (Index i = m - 1; i >= 0; --i) {
for (Index j = 0; j < n; ++j) {
Scalar AX;
if (i == m - 1) {
AX = 0;
} else {
Matrix<Scalar, 1, 1> AXmatrix = A.row(i).tail(m - 1 - i) * X.col(j).tail(m - 1 - i);
AX = AXmatrix(0, 0);
}
Scalar XB;
if (j == 0) {
XB = 0;
} else {
Matrix<Scalar, 1, 1> XBmatrix = X.row(i).head(j) * B.col(j).head(j);
XB = XBmatrix(0, 0);
}
X(i, j) = (C(i, j) - AX - XB) / (A(i, i) + B(j, j));
}
}
return X;
}
template <typename MatrixType, typename VectorType>
void matrix_function_compute_above_diagonal(const MatrixType& T, const VectorType& blockStart,
const VectorType& clusterSize, MatrixType& fT) {
typedef internal::traits<MatrixType> Traits;
typedef typename MatrixType::Scalar Scalar;
static const int Options = MatrixType::Options;
typedef Matrix<Scalar, Dynamic, Dynamic, Options, Traits::RowsAtCompileTime, Traits::ColsAtCompileTime> DynMatrixType;
for (Index k = 1; k < clusterSize.rows(); k++) {
for (Index i = 0; i < clusterSize.rows() - k; i++) {
DynMatrixType A = T.block(blockStart(i), blockStart(i), clusterSize(i), clusterSize(i));
DynMatrixType B = -T.block(blockStart(i + k), blockStart(i + k), clusterSize(i + k), clusterSize(i + k));
DynMatrixType C = fT.block(blockStart(i), blockStart(i), clusterSize(i), clusterSize(i)) *
T.block(blockStart(i), blockStart(i + k), clusterSize(i), clusterSize(i + k));
C -= T.block(blockStart(i), blockStart(i + k), clusterSize(i), clusterSize(i + k)) *
fT.block(blockStart(i + k), blockStart(i + k), clusterSize(i + k), clusterSize(i + k));
for (Index m = i + 1; m < i + k; m++) {
C += fT.block(blockStart(i), blockStart(m), clusterSize(i), clusterSize(m)) *
T.block(blockStart(m), blockStart(i + k), clusterSize(m), clusterSize(i + k));
C -= T.block(blockStart(i), blockStart(m), clusterSize(i), clusterSize(m)) *
fT.block(blockStart(m), blockStart(i + k), clusterSize(m), clusterSize(i + k));
}
fT.block(blockStart(i), blockStart(i + k), clusterSize(i), clusterSize(i + k)) =
matrix_function_solve_triangular_sylvester(A, B, C);
}
}
}
template <typename MatrixType, int IsComplex = NumTraits<typename internal::traits<MatrixType>::Scalar>::IsComplex>
struct matrix_function_compute {
template <typename AtomicType, typename ResultType>
static void run(const MatrixType& A, AtomicType& atomic, ResultType& result);
};
template <typename MatrixType>
struct matrix_function_compute<MatrixType, 0> {
template <typename MatA, typename AtomicType, typename ResultType>
static void run(const MatA& A, AtomicType& atomic, ResultType& result) {
typedef internal::traits<MatrixType> Traits;
typedef typename Traits::Scalar Scalar;
static const int Rows = Traits::RowsAtCompileTime, Cols = Traits::ColsAtCompileTime;
static const int MaxRows = Traits::MaxRowsAtCompileTime, MaxCols = Traits::MaxColsAtCompileTime;
typedef internal::make_complex_t<Scalar> ComplexScalar;
typedef Matrix<ComplexScalar, Rows, Cols, 0, MaxRows, MaxCols> ComplexMatrix;
ComplexMatrix CA = A.template cast<ComplexScalar>();
ComplexMatrix Cresult;
matrix_function_compute<ComplexMatrix>::run(CA, atomic, Cresult);
result = Cresult.real();
}
};
template <typename MatrixType>
struct matrix_function_compute<MatrixType, 1> {
template <typename MatA, typename AtomicType, typename ResultType>
static void run(const MatA& A, AtomicType& atomic, ResultType& result) {
typedef internal::traits<MatrixType> Traits;
const ComplexSchur<MatrixType> schurOfA(A);
eigen_assert(schurOfA.info() == Success);
MatrixType T = schurOfA.matrixT();
MatrixType U = schurOfA.matrixU();
std::list<std::list<Index> > clusters;
matrix_function_partition_eigenvalues(T.diagonal(), clusters);
Matrix<Index, Dynamic, 1> clusterSize;
matrix_function_compute_cluster_size(clusters, clusterSize);
Matrix<Index, Dynamic, 1> blockStart;
matrix_function_compute_block_start(clusterSize, blockStart);
Matrix<Index, Dynamic, 1> eivalToCluster;
matrix_function_compute_map(T.diagonal(), clusters, eivalToCluster);
Matrix<Index, Traits::RowsAtCompileTime, 1> permutation;
matrix_function_compute_permutation(blockStart, eivalToCluster, permutation);
matrix_function_permute_schur(permutation, U, T);
MatrixType fT; matrix_function_compute_block_atomic(T, atomic, blockStart, clusterSize, fT);
matrix_function_compute_above_diagonal(T, blockStart, clusterSize, fT);
result = U * (fT.template triangularView<Upper>() * U.adjoint());
}
};
}
template <typename Derived>
class MatrixFunctionReturnValue : public ReturnByValue<MatrixFunctionReturnValue<Derived> > {
public:
typedef typename Derived::Scalar Scalar;
typedef typename internal::stem_function<Scalar>::type StemFunction;
protected:
typedef typename internal::ref_selector<Derived>::type DerivedNested;
public:
MatrixFunctionReturnValue(const Derived& A, StemFunction f) : m_A(A), m_f(f) {}
template <typename ResultType>
inline void evalTo(ResultType& result) const {
typedef typename internal::nested_eval<Derived, 10>::type NestedEvalType;
typedef internal::remove_all_t<NestedEvalType> NestedEvalTypeClean;
typedef internal::traits<NestedEvalTypeClean> Traits;
typedef internal::make_complex_t<Scalar> ComplexScalar;
typedef Matrix<ComplexScalar, Dynamic, Dynamic, 0, Traits::RowsAtCompileTime, Traits::ColsAtCompileTime>
DynMatrixType;
typedef internal::MatrixFunctionAtomic<DynMatrixType> AtomicType;
AtomicType atomic(m_f);
internal::matrix_function_compute<typename NestedEvalTypeClean::PlainObject>::run(m_A, atomic, result);
}
Index rows() const { return m_A.rows(); }
Index cols() const { return m_A.cols(); }
private:
const DerivedNested m_A;
StemFunction* m_f;
};
namespace internal {
template <typename Derived>
struct traits<MatrixFunctionReturnValue<Derived> > {
typedef typename Derived::PlainObject ReturnType;
};
}
template <typename Derived>
const MatrixFunctionReturnValue<Derived> MatrixBase<Derived>::matrixFunction(
typename internal::stem_function<typename internal::traits<Derived>::Scalar>::type f) const {
eigen_assert(rows() == cols());
return MatrixFunctionReturnValue<Derived>(derived(), f);
}
template <typename Derived>
const MatrixFunctionReturnValue<Derived> MatrixBase<Derived>::sin() const {
eigen_assert(rows() == cols());
typedef typename internal::stem_function<Scalar>::ComplexScalar ComplexScalar;
return MatrixFunctionReturnValue<Derived>(derived(), internal::stem_function_sin<ComplexScalar>);
}
template <typename Derived>
const MatrixFunctionReturnValue<Derived> MatrixBase<Derived>::cos() const {
eigen_assert(rows() == cols());
typedef typename internal::stem_function<Scalar>::ComplexScalar ComplexScalar;
return MatrixFunctionReturnValue<Derived>(derived(), internal::stem_function_cos<ComplexScalar>);
}
template <typename Derived>
const MatrixFunctionReturnValue<Derived> MatrixBase<Derived>::sinh() const {
eigen_assert(rows() == cols());
typedef typename internal::stem_function<Scalar>::ComplexScalar ComplexScalar;
return MatrixFunctionReturnValue<Derived>(derived(), internal::stem_function_sinh<ComplexScalar>);
}
template <typename Derived>
const MatrixFunctionReturnValue<Derived> MatrixBase<Derived>::cosh() const {
eigen_assert(rows() == cols());
typedef typename internal::stem_function<Scalar>::ComplexScalar ComplexScalar;
return MatrixFunctionReturnValue<Derived>(derived(), internal::stem_function_cosh<ComplexScalar>);
}
}
#endif