Loading...
Searching...
No Matches
MatrixFunction.h
1// This file is part of Eigen, a lightweight C++ template library
2// for linear algebra.
3//
4// Copyright (C) 2009-2011, 2013 Jitse Niesen <jitse@maths.leeds.ac.uk>
5//
6// This Source Code Form is subject to the terms of the Mozilla
7// Public License v. 2.0. If a copy of the MPL was not distributed
8// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
9
10#ifndef EIGEN_MATRIX_FUNCTION
11#define EIGEN_MATRIX_FUNCTION
12
13#include "StemFunction.h"
14
15
16namespace Eigen {
17
18namespace internal {
19
21static const float matrix_function_separation = 0.1f;
22
29template <typename MatrixType>
30class MatrixFunctionAtomic
31{
32 public:
33
34 typedef typename MatrixType::Scalar Scalar;
35 typedef typename stem_function<Scalar>::type StemFunction;
36
40 MatrixFunctionAtomic(StemFunction f) : m_f(f) { }
41
46 MatrixType compute(const MatrixType& A);
47
48 private:
49 StemFunction* m_f;
50};
51
52template <typename MatrixType>
53typename NumTraits<typename MatrixType::Scalar>::Real matrix_function_compute_mu(const MatrixType& A)
54{
55 typedef typename plain_col_type<MatrixType>::type VectorType;
56 typename MatrixType::Index rows = A.rows();
57 const MatrixType N = MatrixType::Identity(rows, rows) - A;
58 VectorType e = VectorType::Ones(rows);
59 N.template triangularView<Upper>().solveInPlace(e);
60 return e.cwiseAbs().maxCoeff();
61}
62
63template <typename MatrixType>
64MatrixType MatrixFunctionAtomic<MatrixType>::compute(const MatrixType& A)
65{
66 // TODO: Use that A is upper triangular
67 typedef typename NumTraits<Scalar>::Real RealScalar;
68 typedef typename MatrixType::Index Index;
69 Index rows = A.rows();
70 Scalar avgEival = A.trace() / Scalar(RealScalar(rows));
71 MatrixType Ashifted = A - avgEival * MatrixType::Identity(rows, rows);
72 RealScalar mu = matrix_function_compute_mu(Ashifted);
73 MatrixType F = m_f(avgEival, 0) * MatrixType::Identity(rows, rows);
74 MatrixType P = Ashifted;
75 MatrixType Fincr;
76 for (Index s = 1; s < 1.1 * rows + 10; s++) { // upper limit is fairly arbitrary
77 Fincr = m_f(avgEival, static_cast<int>(s)) * P;
78 F += Fincr;
79 P = Scalar(RealScalar(1.0/(s + 1))) * P * Ashifted;
80
81 // test whether Taylor series converged
82 const RealScalar F_norm = F.cwiseAbs().rowwise().sum().maxCoeff();
83 const RealScalar Fincr_norm = Fincr.cwiseAbs().rowwise().sum().maxCoeff();
84 if (Fincr_norm < NumTraits<Scalar>::epsilon() * F_norm) {
85 RealScalar delta = 0;
86 RealScalar rfactorial = 1;
87 for (Index r = 0; r < rows; r++) {
88 RealScalar mx = 0;
89 for (Index i = 0; i < rows; i++)
90 mx = (std::max)(mx, std::abs(m_f(Ashifted(i, i) + avgEival, static_cast<int>(s+r))));
91 if (r != 0)
92 rfactorial *= RealScalar(r);
93 delta = (std::max)(delta, mx / rfactorial);
94 }
95 const RealScalar P_norm = P.cwiseAbs().rowwise().sum().maxCoeff();
96 if (mu * delta * P_norm < NumTraits<Scalar>::epsilon() * F_norm) // series converged
97 break;
98 }
99 }
100 return F;
101}
102
108template <typename Index, typename ListOfClusters>
109typename ListOfClusters::iterator matrix_function_find_cluster(Index key, ListOfClusters& clusters)
110{
111 typename std::list<Index>::iterator j;
112 for (typename ListOfClusters::iterator i = clusters.begin(); i != clusters.end(); ++i) {
113 j = std::find(i->begin(), i->end(), key);
114 if (j != i->end())
115 return i;
116 }
117 return clusters.end();
118}
119
131template <typename EivalsType, typename Cluster>
132void matrix_function_partition_eigenvalues(const EivalsType& eivals, std::list<Cluster>& clusters)
133{
134 typedef typename EivalsType::Index Index;
135 typedef typename EivalsType::RealScalar RealScalar;
136 for (Index i=0; i<eivals.rows(); ++i) {
137 // Find cluster containing i-th ei'val, adding a new cluster if necessary
138 typename std::list<Cluster>::iterator qi = matrix_function_find_cluster(i, clusters);
139 if (qi == clusters.end()) {
140 Cluster l;
141 l.push_back(i);
142 clusters.push_back(l);
143 qi = clusters.end();
144 --qi;
145 }
146
147 // Look for other element to add to the set
148 for (Index j=i+1; j<eivals.rows(); ++j) {
149 if (abs(eivals(j) - eivals(i)) <= RealScalar(matrix_function_separation)
150 && std::find(qi->begin(), qi->end(), j) == qi->end()) {
151 typename std::list<Cluster>::iterator qj = matrix_function_find_cluster(j, clusters);
152 if (qj == clusters.end()) {
153 qi->push_back(j);
154 } else {
155 qi->insert(qi->end(), qj->begin(), qj->end());
156 clusters.erase(qj);
157 }
158 }
159 }
160 }
161}
162
164template <typename ListOfClusters, typename Index>
165void matrix_function_compute_cluster_size(const ListOfClusters& clusters, Matrix<Index, Dynamic, 1>& clusterSize)
166{
167 const Index numClusters = static_cast<Index>(clusters.size());
168 clusterSize.setZero(numClusters);
169 Index clusterIndex = 0;
170 for (typename ListOfClusters::const_iterator cluster = clusters.begin(); cluster != clusters.end(); ++cluster) {
171 clusterSize[clusterIndex] = cluster->size();
172 ++clusterIndex;
173 }
174}
175
177template <typename VectorType>
178void matrix_function_compute_block_start(const VectorType& clusterSize, VectorType& blockStart)
179{
180 blockStart.resize(clusterSize.rows());
181 blockStart(0) = 0;
182 for (typename VectorType::Index i = 1; i < clusterSize.rows(); i++) {
183 blockStart(i) = blockStart(i-1) + clusterSize(i-1);
184 }
185}
186
188template <typename EivalsType, typename ListOfClusters, typename VectorType>
189void matrix_function_compute_map(const EivalsType& eivals, const ListOfClusters& clusters, VectorType& eivalToCluster)
190{
191 typedef typename EivalsType::Index Index;
192 eivalToCluster.resize(eivals.rows());
193 Index clusterIndex = 0;
194 for (typename ListOfClusters::const_iterator cluster = clusters.begin(); cluster != clusters.end(); ++cluster) {
195 for (Index i = 0; i < eivals.rows(); ++i) {
196 if (std::find(cluster->begin(), cluster->end(), i) != cluster->end()) {
197 eivalToCluster[i] = clusterIndex;
198 }
199 }
200 ++clusterIndex;
201 }
202}
203
205template <typename DynVectorType, typename VectorType>
206void matrix_function_compute_permutation(const DynVectorType& blockStart, const DynVectorType& eivalToCluster, VectorType& permutation)
207{
208 typedef typename VectorType::Index Index;
209 DynVectorType indexNextEntry = blockStart;
210 permutation.resize(eivalToCluster.rows());
211 for (Index i = 0; i < eivalToCluster.rows(); i++) {
212 Index cluster = eivalToCluster[i];
213 permutation[i] = indexNextEntry[cluster];
214 ++indexNextEntry[cluster];
215 }
216}
217
219template <typename VectorType, typename MatrixType>
220void matrix_function_permute_schur(VectorType& permutation, MatrixType& U, MatrixType& T)
221{
222 typedef typename VectorType::Index Index;
223 for (Index i = 0; i < permutation.rows() - 1; i++) {
224 Index j;
225 for (j = i; j < permutation.rows(); j++) {
226 if (permutation(j) == i) break;
227 }
228 eigen_assert(permutation(j) == i);
229 for (Index k = j-1; k >= i; k--) {
230 JacobiRotation<typename MatrixType::Scalar> rotation;
231 rotation.makeGivens(T(k, k+1), T(k+1, k+1) - T(k, k));
232 T.applyOnTheLeft(k, k+1, rotation.adjoint());
233 T.applyOnTheRight(k, k+1, rotation);
234 U.applyOnTheRight(k, k+1, rotation);
235 std::swap(permutation.coeffRef(k), permutation.coeffRef(k+1));
236 }
237 }
238}
239
246template <typename MatrixType, typename AtomicType, typename VectorType>
247void matrix_function_compute_block_atomic(const MatrixType& T, AtomicType& atomic, const VectorType& blockStart, const VectorType& clusterSize, MatrixType& fT)
248{
249 fT.setZero(T.rows(), T.cols());
250 for (typename VectorType::Index i = 0; i < clusterSize.rows(); ++i) {
251 fT.block(blockStart(i), blockStart(i), clusterSize(i), clusterSize(i))
252 = atomic.compute(T.block(blockStart(i), blockStart(i), clusterSize(i), clusterSize(i)));
253 }
254}
255
278template <typename MatrixType>
279MatrixType matrix_function_solve_triangular_sylvester(const MatrixType& A, const MatrixType& B, const MatrixType& C)
280{
281 eigen_assert(A.rows() == A.cols());
282 eigen_assert(A.isUpperTriangular());
283 eigen_assert(B.rows() == B.cols());
284 eigen_assert(B.isUpperTriangular());
285 eigen_assert(C.rows() == A.rows());
286 eigen_assert(C.cols() == B.rows());
287
288 typedef typename MatrixType::Index Index;
289 typedef typename MatrixType::Scalar Scalar;
290
291 Index m = A.rows();
292 Index n = B.rows();
293 MatrixType X(m, n);
294
295 for (Index i = m - 1; i >= 0; --i) {
296 for (Index j = 0; j < n; ++j) {
297
298 // Compute AX = \sum_{k=i+1}^m A_{ik} X_{kj}
299 Scalar AX;
300 if (i == m - 1) {
301 AX = 0;
302 } else {
303 Matrix<Scalar,1,1> AXmatrix = A.row(i).tail(m-1-i) * X.col(j).tail(m-1-i);
304 AX = AXmatrix(0,0);
305 }
306
307 // Compute XB = \sum_{k=1}^{j-1} X_{ik} B_{kj}
308 Scalar XB;
309 if (j == 0) {
310 XB = 0;
311 } else {
312 Matrix<Scalar,1,1> XBmatrix = X.row(i).head(j) * B.col(j).head(j);
313 XB = XBmatrix(0,0);
314 }
315
316 X(i,j) = (C(i,j) - AX - XB) / (A(i,i) + B(j,j));
317 }
318 }
319 return X;
320}
321
328template <typename MatrixType, typename VectorType>
329void matrix_function_compute_above_diagonal(const MatrixType& T, const VectorType& blockStart, const VectorType& clusterSize, MatrixType& fT)
330{
331 typedef internal::traits<MatrixType> Traits;
332 typedef typename MatrixType::Scalar Scalar;
333 typedef typename MatrixType::Index Index;
334 static const int RowsAtCompileTime = Traits::RowsAtCompileTime;
335 static const int ColsAtCompileTime = Traits::ColsAtCompileTime;
336 static const int Options = MatrixType::Options;
337 typedef Matrix<Scalar, Dynamic, Dynamic, Options, RowsAtCompileTime, ColsAtCompileTime> DynMatrixType;
338
339 for (Index k = 1; k < clusterSize.rows(); k++) {
340 for (Index i = 0; i < clusterSize.rows() - k; i++) {
341 // compute (i, i+k) block
342 DynMatrixType A = T.block(blockStart(i), blockStart(i), clusterSize(i), clusterSize(i));
343 DynMatrixType B = -T.block(blockStart(i+k), blockStart(i+k), clusterSize(i+k), clusterSize(i+k));
344 DynMatrixType C = fT.block(blockStart(i), blockStart(i), clusterSize(i), clusterSize(i))
345 * T.block(blockStart(i), blockStart(i+k), clusterSize(i), clusterSize(i+k));
346 C -= T.block(blockStart(i), blockStart(i+k), clusterSize(i), clusterSize(i+k))
347 * fT.block(blockStart(i+k), blockStart(i+k), clusterSize(i+k), clusterSize(i+k));
348 for (Index m = i + 1; m < i + k; m++) {
349 C += fT.block(blockStart(i), blockStart(m), clusterSize(i), clusterSize(m))
350 * T.block(blockStart(m), blockStart(i+k), clusterSize(m), clusterSize(i+k));
351 C -= T.block(blockStart(i), blockStart(m), clusterSize(i), clusterSize(m))
352 * fT.block(blockStart(m), blockStart(i+k), clusterSize(m), clusterSize(i+k));
353 }
354 fT.block(blockStart(i), blockStart(i+k), clusterSize(i), clusterSize(i+k))
356 }
357 }
358}
359
375template <typename MatrixType, int IsComplex = NumTraits<typename internal::traits<MatrixType>::Scalar>::IsComplex>
376struct matrix_function_compute
377{
388 template <typename AtomicType, typename ResultType>
389 static void run(const MatrixType& A, AtomicType& atomic, ResultType &result);
390};
391
398template <typename MatrixType>
399struct matrix_function_compute<MatrixType, 0>
400{
401 template <typename AtomicType, typename ResultType>
402 static void run(const MatrixType& A, AtomicType& atomic, ResultType &result)
403 {
404 typedef internal::traits<MatrixType> Traits;
405 typedef typename Traits::Scalar Scalar;
406 static const int Rows = Traits::RowsAtCompileTime, Cols = Traits::ColsAtCompileTime;
407 static const int MaxRows = Traits::MaxRowsAtCompileTime, MaxCols = Traits::MaxColsAtCompileTime;
408
409 typedef std::complex<Scalar> ComplexScalar;
410 typedef Matrix<ComplexScalar, Rows, Cols, 0, MaxRows, MaxCols> ComplexMatrix;
411
412 ComplexMatrix CA = A.template cast<ComplexScalar>();
413 ComplexMatrix Cresult;
414 matrix_function_compute<ComplexMatrix>::run(CA, atomic, Cresult);
415 result = Cresult.real();
416 }
417};
418
422template <typename MatrixType>
423struct matrix_function_compute<MatrixType, 1>
424{
425 template <typename AtomicType, typename ResultType>
426 static void run(const MatrixType& A, AtomicType& atomic, ResultType &result)
427 {
428 typedef internal::traits<MatrixType> Traits;
429 typedef typename MatrixType::Index Index;
430
431 // compute Schur decomposition of A
432 const ComplexSchur<MatrixType> schurOfA(A);
433 MatrixType T = schurOfA.matrixT();
434 MatrixType U = schurOfA.matrixU();
435
436 // partition eigenvalues into clusters of ei'vals "close" to each other
437 std::list<std::list<Index> > clusters;
438 matrix_function_partition_eigenvalues(T.diagonal(), clusters);
439
440 // compute size of each cluster
441 Matrix<Index, Dynamic, 1> clusterSize;
442 matrix_function_compute_cluster_size(clusters, clusterSize);
443
444 // blockStart[i] is row index at which block corresponding to i-th cluster starts
445 Matrix<Index, Dynamic, 1> blockStart;
446 matrix_function_compute_block_start(clusterSize, blockStart);
447
448 // compute map so that eivalToCluster[i] = j means that i-th ei'val is in j-th cluster
449 Matrix<Index, Dynamic, 1> eivalToCluster;
450 matrix_function_compute_map(T.diagonal(), clusters, eivalToCluster);
451
452 // compute permutation which groups ei'vals in same cluster together
453 Matrix<Index, Traits::RowsAtCompileTime, 1> permutation;
454 matrix_function_compute_permutation(blockStart, eivalToCluster, permutation);
455
456 // permute Schur decomposition
457 matrix_function_permute_schur(permutation, U, T);
458
459 // compute result
460 MatrixType fT; // matrix function applied to T
461 matrix_function_compute_block_atomic(T, atomic, blockStart, clusterSize, fT);
462 matrix_function_compute_above_diagonal(T, blockStart, clusterSize, fT);
463 result = U * (fT.template triangularView<Upper>() * U.adjoint());
464 }
465};
466
467} // end of namespace internal
468
479template<typename Derived> class MatrixFunctionReturnValue
480: public ReturnByValue<MatrixFunctionReturnValue<Derived> >
481{
482 public:
483 typedef typename Derived::Scalar Scalar;
484 typedef typename Derived::Index Index;
485 typedef typename internal::stem_function<Scalar>::type StemFunction;
486
487 protected:
488 typedef typename internal::ref_selector<Derived>::type DerivedNested;
489
490 public:
491
497 MatrixFunctionReturnValue(const Derived& A, StemFunction f) : m_A(A), m_f(f) { }
498
503 template <typename ResultType>
504 inline void evalTo(ResultType& result) const
505 {
506 typedef typename internal::nested_eval<Derived, 10>::type NestedEvalType;
507 typedef typename internal::remove_all<NestedEvalType>::type NestedEvalTypeClean;
508 typedef internal::traits<NestedEvalTypeClean> Traits;
509 static const int RowsAtCompileTime = Traits::RowsAtCompileTime;
510 static const int ColsAtCompileTime = Traits::ColsAtCompileTime;
511 typedef std::complex<typename NumTraits<Scalar>::Real> ComplexScalar;
512 typedef Matrix<ComplexScalar, Dynamic, Dynamic, 0, RowsAtCompileTime, ColsAtCompileTime> DynMatrixType;
513
514 typedef internal::MatrixFunctionAtomic<DynMatrixType> AtomicType;
515 AtomicType atomic(m_f);
516
517 internal::matrix_function_compute<NestedEvalTypeClean>::run(m_A, atomic, result);
518 }
519
520 Index rows() const { return m_A.rows(); }
521 Index cols() const { return m_A.cols(); }
522
523 private:
524 const DerivedNested m_A;
525 StemFunction *m_f;
526};
527
528namespace internal {
529template<typename Derived>
530struct traits<MatrixFunctionReturnValue<Derived> >
531{
532 typedef typename Derived::PlainObject ReturnType;
533};
534}
535
536
537/********** MatrixBase methods **********/
538
539
540template <typename Derived>
541const MatrixFunctionReturnValue<Derived> MatrixBase<Derived>::matrixFunction(typename internal::stem_function<typename internal::traits<Derived>::Scalar>::type f) const
542{
543 eigen_assert(rows() == cols());
544 return MatrixFunctionReturnValue<Derived>(derived(), f);
545}
546
547template <typename Derived>
548const MatrixFunctionReturnValue<Derived> MatrixBase<Derived>::sin() const
549{
550 eigen_assert(rows() == cols());
551 typedef typename internal::stem_function<Scalar>::ComplexScalar ComplexScalar;
552 return MatrixFunctionReturnValue<Derived>(derived(), internal::stem_function_sin<ComplexScalar>);
553}
554
555template <typename Derived>
556const MatrixFunctionReturnValue<Derived> MatrixBase<Derived>::cos() const
557{
558 eigen_assert(rows() == cols());
559 typedef typename internal::stem_function<Scalar>::ComplexScalar ComplexScalar;
560 return MatrixFunctionReturnValue<Derived>(derived(), internal::stem_function_cos<ComplexScalar>);
561}
562
563template <typename Derived>
564const MatrixFunctionReturnValue<Derived> MatrixBase<Derived>::sinh() const
565{
566 eigen_assert(rows() == cols());
567 typedef typename internal::stem_function<Scalar>::ComplexScalar ComplexScalar;
568 return MatrixFunctionReturnValue<Derived>(derived(), internal::stem_function_sinh<ComplexScalar>);
569}
570
571template <typename Derived>
572const MatrixFunctionReturnValue<Derived> MatrixBase<Derived>::cosh() const
573{
574 eigen_assert(rows() == cols());
575 typedef typename internal::stem_function<Scalar>::ComplexScalar ComplexScalar;
576 return MatrixFunctionReturnValue<Derived>(derived(), internal::stem_function_cosh<ComplexScalar>);
577}
578
579} // end namespace Eigen
580
581#endif // EIGEN_MATRIX_FUNCTION
Proxy for the matrix function of some matrix (expression).
Definition: MatrixFunction.h:481
void evalTo(ResultType &result) const
Compute the matrix function.
Definition: MatrixFunction.h:504
MatrixFunctionReturnValue(const Derived &A, StemFunction f)
Constructor.
Definition: MatrixFunction.h:497
void matrix_function_compute_permutation(const DynVectorType &blockStart, const DynVectorType &eivalToCluster, VectorType &permutation)
Compute permutation which groups ei'vals in same cluster together.
Definition: MatrixFunction.h:206
void matrix_function_compute_cluster_size(const ListOfClusters &clusters, Matrix< Index, Dynamic, 1 > &clusterSize)
Compute size of each cluster given a partitioning.
Definition: MatrixFunction.h:165
void matrix_function_compute_block_start(const VectorType &clusterSize, VectorType &blockStart)
Compute start of each block using clusterSize.
Definition: MatrixFunction.h:178
void matrix_function_compute_block_atomic(const MatrixType &T, AtomicType &atomic, const VectorType &blockStart, const VectorType &clusterSize, MatrixType &fT)
Compute block diagonal part of matrix function.
Definition: MatrixFunction.h:247
void matrix_function_permute_schur(VectorType &permutation, MatrixType &U, MatrixType &T)
Permute Schur decomposition in U and T according to permutation.
Definition: MatrixFunction.h:220
void matrix_function_compute_above_diagonal(const MatrixType &T, const VectorType &blockStart, const VectorType &clusterSize, MatrixType &fT)
Compute part of matrix function above block diagonal.
Definition: MatrixFunction.h:329
void matrix_function_partition_eigenvalues(const EivalsType &eivals, std::list< Cluster > &clusters)
Partition eigenvalues in clusters of ei'vals close to each other.
Definition: MatrixFunction.h:132
MatrixType matrix_function_solve_triangular_sylvester(const MatrixType &A, const MatrixType &B, const MatrixType &C)
Solve a triangular Sylvester equation AX + XB = C.
Definition: MatrixFunction.h:279
void matrix_function_compute_map(const EivalsType &eivals, const ListOfClusters &clusters, VectorType &eivalToCluster)
Compute mapping of eigenvalue indices to cluster indices.
Definition: MatrixFunction.h:189
ListOfClusters::iterator matrix_function_find_cluster(Index key, ListOfClusters &clusters)
Find cluster in clusters containing some value.
Definition: MatrixFunction.h:109
Namespace containing all symbols from the Eigen library.
Definition: AdolcForward:45