Eigen-Contrib  5.0.1
 
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// SPDX-License-Identifier: MPL-2.0
10
11#ifndef EIGEN_MATRIX_FUNCTION_H
12#define EIGEN_MATRIX_FUNCTION_H
13
14#include "StemFunction.h"
15
16// IWYU pragma: private
17#include "./InternalHeaderCheck.h"
18
19namespace Eigen {
20
21namespace internal {
22
24static const float matrix_function_separation = 0.1f;
25
32template <typename MatrixType>
34 public:
35 typedef typename MatrixType::Scalar Scalar;
36 typedef typename stem_function<Scalar>::type StemFunction;
37
41 MatrixFunctionAtomic(StemFunction f) : m_f(f) {}
42
47 MatrixType compute(const MatrixType& A);
48
49 private:
50 StemFunction* m_f;
51};
52
53template <typename MatrixType>
54typename NumTraits<typename MatrixType::Scalar>::Real matrix_function_compute_mu(const MatrixType& A) {
55 typedef typename plain_col_type<MatrixType>::type VectorType;
56 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 // TODO: Use that A is upper triangular
66 typedef typename NumTraits<Scalar>::Real RealScalar;
67 Index rows = A.rows();
68 Scalar avgEival = A.trace() / Scalar(RealScalar(rows));
69 MatrixType Ashifted = A - avgEival * MatrixType::Identity(rows, rows);
70 RealScalar mu = matrix_function_compute_mu(Ashifted);
71 MatrixType F = m_f(avgEival, 0) * MatrixType::Identity(rows, rows);
72 MatrixType P = Ashifted;
73 MatrixType Fincr;
74 // Reaching epsilon takes more terms as the mantissa lengthens, never fewer than ten.
75 const Index extraIterations = numext::maxi<Index>(10, NumTraits<RealScalar>::digits());
76 // s < 1.1 * rows + extraIterations, both sides scaled by ten to compare exactly.
77 for (Index s = 1; 10 * s < 11 * rows + 10 * extraIterations; ++s) {
78 Fincr = m_f(avgEival, static_cast<int>(s)) * P;
79 F += Fincr;
80 P = Scalar(RealScalar(1) / RealScalar(s + 1)) * P * Ashifted;
81
82 // test whether Taylor series converged
83 const RealScalar F_norm = F.cwiseAbs().rowwise().sum().maxCoeff();
84 const RealScalar Fincr_norm = Fincr.cwiseAbs().rowwise().sum().maxCoeff();
85 if (Fincr_norm < NumTraits<Scalar>::epsilon() * F_norm) {
86 RealScalar delta = 0;
87 RealScalar rfactorial = 1;
88 for (Index r = 0; r < rows; r++) {
89 RealScalar mx = 0;
90 for (Index i = 0; i < rows; i++)
91 mx = (std::max)(mx, std::abs(m_f(Ashifted(i, i) + avgEival, static_cast<int>(s + r))));
92 if (r != 0) rfactorial *= RealScalar(r);
93 delta = (std::max)(delta, RealScalar(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 typename std::list<Index>::iterator j;
111 for (typename ListOfClusters::iterator i = clusters.begin(); i != clusters.end(); ++i) {
112 j = std::find(i->begin(), i->end(), key);
113 if (j != i->end()) return i;
114 }
115 return clusters.end();
116}
117
129template <typename EivalsType, typename Cluster>
130void matrix_function_partition_eigenvalues(const EivalsType& eivals, std::list<Cluster>& clusters) {
131 typedef typename EivalsType::RealScalar RealScalar;
132 for (Index i = 0; i < eivals.rows(); ++i) {
133 // Find cluster containing i-th ei'val, adding a new cluster if necessary
134 typename std::list<Cluster>::iterator qi = matrix_function_find_cluster(i, clusters);
135 if (qi == clusters.end()) {
136 Cluster l;
137 l.push_back(i);
138 clusters.push_back(l);
139 qi = clusters.end();
140 --qi;
141 }
142
143 // Look for other element to add to the set
144 for (Index j = i + 1; j < eivals.rows(); ++j) {
145 if (abs(eivals(j) - eivals(i)) <= RealScalar(matrix_function_separation) &&
146 std::find(qi->begin(), qi->end(), j) == qi->end()) {
147 typename std::list<Cluster>::iterator qj = matrix_function_find_cluster(j, clusters);
148 if (qj == clusters.end()) {
149 qi->push_back(j);
150 } else {
151 qi->insert(qi->end(), qj->begin(), qj->end());
152 clusters.erase(qj);
153 }
154 }
155 }
156 }
157}
158
160template <typename ListOfClusters, typename Index>
161void matrix_function_compute_cluster_size(const ListOfClusters& clusters, Matrix<Index, Dynamic, 1>& clusterSize) {
162 const Index numClusters = static_cast<Index>(clusters.size());
163 clusterSize.setZero(numClusters);
164 Index clusterIndex = 0;
165 for (typename ListOfClusters::const_iterator cluster = clusters.begin(); cluster != clusters.end(); ++cluster) {
166 clusterSize[clusterIndex] = cluster->size();
167 ++clusterIndex;
168 }
169}
170
172template <typename VectorType>
173void matrix_function_compute_block_start(const VectorType& clusterSize, VectorType& blockStart) {
174 blockStart.resize(clusterSize.rows());
175 blockStart(0) = 0;
176 for (Index i = 1; i < clusterSize.rows(); i++) {
177 blockStart(i) = blockStart(i - 1) + clusterSize(i - 1);
178 }
179}
180
182template <typename EivalsType, typename ListOfClusters, typename VectorType>
183void matrix_function_compute_map(const EivalsType& eivals, const ListOfClusters& clusters, VectorType& eivalToCluster) {
184 eivalToCluster.resize(eivals.rows());
185 Index clusterIndex = 0;
186 for (typename ListOfClusters::const_iterator cluster = clusters.begin(); cluster != clusters.end(); ++cluster) {
187 for (Index i = 0; i < eivals.rows(); ++i) {
188 if (std::find(cluster->begin(), cluster->end(), i) != cluster->end()) {
189 eivalToCluster[i] = clusterIndex;
190 }
191 }
192 ++clusterIndex;
193 }
194}
195
197template <typename DynVectorType, typename VectorType>
198void matrix_function_compute_permutation(const DynVectorType& blockStart, const DynVectorType& eivalToCluster,
199 VectorType& permutation) {
200 DynVectorType indexNextEntry = blockStart;
201 permutation.resize(eivalToCluster.rows());
202 for (Index i = 0; i < eivalToCluster.rows(); i++) {
203 Index cluster = eivalToCluster[i];
204 permutation[i] = indexNextEntry[cluster];
205 ++indexNextEntry[cluster];
206 }
207}
208
210template <typename VectorType, typename MatrixType>
211void matrix_function_permute_schur(VectorType& permutation, MatrixType& U, MatrixType& T) {
212 for (Index i = 0; i < permutation.rows() - 1; i++) {
213 Index j;
214 for (j = i; j < permutation.rows(); j++) {
215 if (permutation(j) == i) break;
216 }
217 eigen_assert(permutation(j) == i);
218 for (Index k = j - 1; k >= i; k--) {
219 JacobiRotation<typename MatrixType::Scalar> rotation;
220 rotation.makeGivens(T(k, k + 1), T(k + 1, k + 1) - T(k, k));
221 T.applyOnTheLeft(k, k + 1, rotation.adjoint());
222 T.applyOnTheRight(k, k + 1, rotation);
223 U.applyOnTheRight(k, k + 1, rotation);
224 std::swap(permutation.coeffRef(k), permutation.coeffRef(k + 1));
225 }
226 }
227}
228
235template <typename MatrixType, typename AtomicType, typename VectorType>
236void matrix_function_compute_block_atomic(const MatrixType& T, AtomicType& atomic, const VectorType& blockStart,
237 const VectorType& clusterSize, MatrixType& fT) {
238 fT.setZero(T.rows(), T.cols());
239 for (Index i = 0; i < clusterSize.rows(); ++i) {
240 fT.block(blockStart(i), blockStart(i), clusterSize(i), clusterSize(i)) =
241 atomic.compute(T.block(blockStart(i), blockStart(i), clusterSize(i), clusterSize(i)));
242 }
243}
244
267template <typename MatrixType>
268MatrixType matrix_function_solve_triangular_sylvester(const MatrixType& A, const MatrixType& B, const MatrixType& C) {
269 eigen_assert(A.rows() == A.cols());
270 eigen_assert(A.isUpperTriangular());
271 eigen_assert(B.rows() == B.cols());
272 eigen_assert(B.isUpperTriangular());
273 eigen_assert(C.rows() == A.rows());
274 eigen_assert(C.cols() == B.rows());
275
276 typedef typename MatrixType::Scalar Scalar;
277
278 Index m = A.rows();
279 Index n = B.rows();
280 MatrixType X(m, n);
281
282 for (Index i = m - 1; i >= 0; --i) {
283 for (Index j = 0; j < n; ++j) {
284 // Compute AX = \sum_{k=i+1}^m A_{ik} X_{kj}
285 Scalar AX;
286 if (i == m - 1) {
287 AX = 0;
288 } else {
289 Matrix<Scalar, 1, 1> AXmatrix = A.row(i).tail(m - 1 - i) * X.col(j).tail(m - 1 - i);
290 AX = AXmatrix(0, 0);
291 }
292
293 // Compute XB = \sum_{k=1}^{j-1} X_{ik} B_{kj}
294 Scalar XB;
295 if (j == 0) {
296 XB = 0;
297 } else {
298 Matrix<Scalar, 1, 1> XBmatrix = X.row(i).head(j) * B.col(j).head(j);
299 XB = XBmatrix(0, 0);
300 }
301
302 X(i, j) = (C(i, j) - AX - XB) / (A(i, i) + B(j, j));
303 }
304 }
305 return X;
306}
307
314template <typename MatrixType, typename VectorType>
315void matrix_function_compute_above_diagonal(const MatrixType& T, const VectorType& blockStart,
316 const VectorType& clusterSize, MatrixType& fT) {
317 typedef internal::traits<MatrixType> Traits;
318 typedef typename MatrixType::Scalar Scalar;
319 static const int Options = MatrixType::Options;
320 typedef Matrix<Scalar, Dynamic, Dynamic, Options, Traits::RowsAtCompileTime, Traits::ColsAtCompileTime> DynMatrixType;
321
322 for (Index k = 1; k < clusterSize.rows(); k++) {
323 for (Index i = 0; i < clusterSize.rows() - k; i++) {
324 // compute (i, i+k) block
325 DynMatrixType A = T.block(blockStart(i), blockStart(i), clusterSize(i), clusterSize(i));
326 DynMatrixType B = -T.block(blockStart(i + k), blockStart(i + k), clusterSize(i + k), clusterSize(i + k));
327 DynMatrixType C = fT.block(blockStart(i), blockStart(i), clusterSize(i), clusterSize(i)) *
328 T.block(blockStart(i), blockStart(i + k), clusterSize(i), clusterSize(i + k));
329 C.noalias() -= T.block(blockStart(i), blockStart(i + k), clusterSize(i), clusterSize(i + k)) *
330 fT.block(blockStart(i + k), blockStart(i + k), clusterSize(i + k), clusterSize(i + k));
331 for (Index m = i + 1; m < i + k; m++) {
332 C.noalias() += fT.block(blockStart(i), blockStart(m), clusterSize(i), clusterSize(m)) *
333 T.block(blockStart(m), blockStart(i + k), clusterSize(m), clusterSize(i + k));
334 C.noalias() -= T.block(blockStart(i), blockStart(m), clusterSize(i), clusterSize(m)) *
335 fT.block(blockStart(m), blockStart(i + k), clusterSize(m), clusterSize(i + k));
336 }
337 fT.block(blockStart(i), blockStart(i + k), clusterSize(i), clusterSize(i + k)) =
338 matrix_function_solve_triangular_sylvester(A, B, C);
339 }
340 }
341}
342
358template <typename MatrixType, int IsComplex = NumTraits<typename internal::traits<MatrixType>::Scalar>::IsComplex>
370 template <typename AtomicType, typename ResultType>
371 static void run(const MatrixType& A, AtomicType& atomic, ResultType& result);
372};
373
380template <typename MatrixType>
381struct matrix_function_compute<MatrixType, 0> {
382 template <typename MatA, typename AtomicType, typename ResultType>
383 static void run(const MatA& A, AtomicType& atomic, ResultType& result) {
384 typedef internal::traits<MatrixType> Traits;
385 typedef typename Traits::Scalar Scalar;
386 static const int Rows = Traits::RowsAtCompileTime, Cols = Traits::ColsAtCompileTime;
387 static const int MaxRows = Traits::MaxRowsAtCompileTime, MaxCols = Traits::MaxColsAtCompileTime;
388
389 typedef internal::make_complex_t<Scalar> ComplexScalar;
391
392 ComplexMatrix CA = A.template cast<ComplexScalar>();
393 ComplexMatrix Cresult;
395 result = Cresult.real();
396 }
397};
398
402template <typename MatrixType>
403struct matrix_function_compute<MatrixType, 1> {
404 template <typename MatA, typename AtomicType, typename ResultType>
405 static void run(const MatA& A, AtomicType& atomic, ResultType& result) {
406 typedef internal::traits<MatrixType> Traits;
407
408 // compute Schur decomposition of A
409 const ComplexSchur<MatrixType> schurOfA(A);
410 eigen_assert(schurOfA.info() == Success);
411 MatrixType T = schurOfA.matrixT();
412 MatrixType U = schurOfA.matrixU();
413
414 // partition eigenvalues into clusters of ei'vals "close" to each other
415 std::list<std::list<Index> > clusters;
416 matrix_function_partition_eigenvalues(T.diagonal(), clusters);
417
418 // compute size of each cluster
419 Matrix<Index, Dynamic, 1> clusterSize;
420 matrix_function_compute_cluster_size(clusters, clusterSize);
421
422 // blockStart[i] is row index at which block corresponding to i-th cluster starts
423 Matrix<Index, Dynamic, 1> blockStart;
424 matrix_function_compute_block_start(clusterSize, blockStart);
425
426 // compute map so that eivalToCluster[i] = j means that i-th ei'val is in j-th cluster
427 Matrix<Index, Dynamic, 1> eivalToCluster;
428 matrix_function_compute_map(T.diagonal(), clusters, eivalToCluster);
429
430 // compute permutation which groups ei'vals in same cluster together
431 Matrix<Index, Traits::RowsAtCompileTime, 1> permutation;
432 matrix_function_compute_permutation(blockStart, eivalToCluster, permutation);
433
434 // permute Schur decomposition
435 matrix_function_permute_schur(permutation, U, T);
436
437 // compute result
438 MatrixType fT; // matrix function applied to T
439 matrix_function_compute_block_atomic(T, atomic, blockStart, clusterSize, fT);
440 matrix_function_compute_above_diagonal(T, blockStart, clusterSize, fT);
441 call_assignment_no_alias(result.derived(), U * (fT.template triangularView<Upper>() * U.adjoint()));
442 }
443};
444
445} // end of namespace internal
446
457template <typename Derived>
458class MatrixFunctionReturnValue : public ReturnByValue<MatrixFunctionReturnValue<Derived> > {
459 public:
460 typedef typename Derived::Scalar Scalar;
461 typedef typename internal::stem_function<Scalar>::type StemFunction;
462
463 protected:
464 typedef typename internal::ref_selector<Derived>::type DerivedNested;
465
466 public:
472 MatrixFunctionReturnValue(const Derived& A, StemFunction f) : m_A(A), m_f(f) {}
473
478 template <typename ResultType>
479 inline void evalTo(ResultType& result) const {
480 typedef typename internal::nested_eval<Derived, 10>::type NestedEvalType;
481 typedef internal::remove_all_t<NestedEvalType> NestedEvalTypeClean;
482 typedef internal::traits<NestedEvalTypeClean> Traits;
483 typedef internal::make_complex_t<Scalar> ComplexScalar;
485 DynMatrixType;
486
488 AtomicType atomic(m_f);
489
491 }
492
493 Index rows() const { return m_A.rows(); }
494 Index cols() const { return m_A.cols(); }
495
496 private:
497 DerivedNested m_A;
498 StemFunction* m_f;
499};
500
501namespace internal {
502template <typename Derived>
503struct traits<MatrixFunctionReturnValue<Derived> > {
504 typedef typename Derived::PlainObject ReturnType;
505};
506} // namespace internal
507
508/********** MatrixBase methods **********/
509
510template <typename Derived>
512 typename internal::stem_function<typename internal::traits<Derived>::Scalar>::type f) const {
513 eigen_assert(rows() == cols());
514 return MatrixFunctionReturnValue<Derived>(derived(), f);
515}
516
517template <typename Derived>
519 eigen_assert(rows() == cols());
520 typedef typename internal::stem_function<Scalar>::ComplexScalar ComplexScalar;
521 return MatrixFunctionReturnValue<Derived>(derived(), internal::stem_function_sin<ComplexScalar>);
522}
523
524template <typename Derived>
526 eigen_assert(rows() == cols());
527 typedef typename internal::stem_function<Scalar>::ComplexScalar ComplexScalar;
528 return MatrixFunctionReturnValue<Derived>(derived(), internal::stem_function_cos<ComplexScalar>);
529}
530
531template <typename Derived>
533 eigen_assert(rows() == cols());
534 typedef typename internal::stem_function<Scalar>::ComplexScalar ComplexScalar;
535 return MatrixFunctionReturnValue<Derived>(derived(), internal::stem_function_sinh<ComplexScalar>);
536}
537
538template <typename Derived>
540 eigen_assert(rows() == cols());
541 typedef typename internal::stem_function<Scalar>::ComplexScalar ComplexScalar;
542 return MatrixFunctionReturnValue<Derived>(derived(), internal::stem_function_cosh<ComplexScalar>);
543}
544
545} // end namespace Eigen
546
547#endif // EIGEN_MATRIX_FUNCTION_H
const MatrixFunctionReturnValue< Derived > matrixFunction(StemFunction f) const
Definition MatrixFunction.h:511
const MatrixFunctionReturnValue< Derived > cosh() const
Definition MatrixFunction.h:539
const MatrixFunctionReturnValue< Derived > sin() const
Definition MatrixFunction.h:518
const MatrixFunctionReturnValue< Derived > sinh() const
Definition MatrixFunction.h:532
const MatrixFunctionReturnValue< Derived > cos() const
Definition MatrixFunction.h:525
Proxy for the matrix function of some matrix (expression).
Definition MatrixFunction.h:458
void evalTo(ResultType &result) const
Compute the matrix function.
Definition MatrixFunction.h:479
MatrixFunctionReturnValue(const Derived &A, StemFunction f)
Constructor.
Definition MatrixFunction.h:472
Helper class for computing matrix functions of atomic matrices.
Definition MatrixFunction.h:33
MatrixFunctionAtomic(StemFunction f)
Constructor.
Definition MatrixFunction.h:41
MatrixType compute(const MatrixType &A)
Compute matrix function of atomic matrix.
Definition MatrixFunction.h:64
Namespace containing all symbols from the Eigen library.
Class for computing matrix functions.
Definition MatrixFunction.h:359
static void run(const MatrixType &A, AtomicType &atomic, ResultType &result)
Compute the matrix function.