blob: cc12ab62bae0569a70ebdd6c44e64a1feec48c17 [file] [log] [blame]
Brian Silverman72890c22015-09-19 14:37:37 -04001// This file is part of Eigen, a lightweight C++ template library
2// for linear algebra.
3//
Austin Schuh189376f2018-12-20 22:11:15 +11004// Copyright (C) 2009-2011, 2013 Jitse Niesen <jitse@maths.leeds.ac.uk>
Brian Silverman72890c22015-09-19 14:37:37 -04005//
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
Austin Schuh189376f2018-12-20 22:11:15 +110010#ifndef EIGEN_MATRIX_FUNCTION_H
11#define EIGEN_MATRIX_FUNCTION_H
Brian Silverman72890c22015-09-19 14:37:37 -040012
13#include "StemFunction.h"
Brian Silverman72890c22015-09-19 14:37:37 -040014
15
16namespace Eigen {
17
Austin Schuh189376f2018-12-20 22:11:15 +110018namespace internal {
19
20/** \brief Maximum distance allowed between eigenvalues to be considered "close". */
21static const float matrix_function_separation = 0.1f;
22
Brian Silverman72890c22015-09-19 14:37:37 -040023/** \ingroup MatrixFunctions_Module
Austin Schuh189376f2018-12-20 22:11:15 +110024 * \class MatrixFunctionAtomic
25 * \brief Helper class for computing matrix functions of atomic matrices.
Brian Silverman72890c22015-09-19 14:37:37 -040026 *
Austin Schuh189376f2018-12-20 22:11:15 +110027 * Here, an atomic matrix is a triangular matrix whose diagonal entries are close to each other.
Brian Silverman72890c22015-09-19 14:37:37 -040028 */
Austin Schuh189376f2018-12-20 22:11:15 +110029template <typename MatrixType>
30class MatrixFunctionAtomic
Brian Silverman72890c22015-09-19 14:37:37 -040031{
Austin Schuh189376f2018-12-20 22:11:15 +110032 public:
Brian Silverman72890c22015-09-19 14:37:37 -040033
Brian Silverman72890c22015-09-19 14:37:37 -040034 typedef typename MatrixType::Scalar Scalar;
Austin Schuh189376f2018-12-20 22:11:15 +110035 typedef typename stem_function<Scalar>::type StemFunction;
Brian Silverman72890c22015-09-19 14:37:37 -040036
Austin Schuh189376f2018-12-20 22:11:15 +110037 /** \brief Constructor
38 * \param[in] f matrix function to compute.
39 */
40 MatrixFunctionAtomic(StemFunction f) : m_f(f) { }
Brian Silverman72890c22015-09-19 14:37:37 -040041
Austin Schuh189376f2018-12-20 22:11:15 +110042 /** \brief Compute matrix function of atomic matrix
43 * \param[in] A argument of matrix function, should be upper triangular and atomic
44 * \returns f(A), the matrix function evaluated at the given matrix
45 */
46 MatrixType compute(const MatrixType& A);
Brian Silverman72890c22015-09-19 14:37:37 -040047
48 private:
Austin Schuh189376f2018-12-20 22:11:15 +110049 StemFunction* m_f;
Brian Silverman72890c22015-09-19 14:37:37 -040050};
51
Austin Schuh189376f2018-12-20 22:11:15 +110052template <typename MatrixType>
53typename NumTraits<typename MatrixType::Scalar>::Real matrix_function_compute_mu(const MatrixType& A)
Brian Silverman72890c22015-09-19 14:37:37 -040054{
Austin Schuh189376f2018-12-20 22:11:15 +110055 typedef typename plain_col_type<MatrixType>::type VectorType;
Austin Schuhc55b0172022-02-20 17:52:35 -080056 Index rows = A.rows();
Austin Schuh189376f2018-12-20 22:11:15 +110057 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();
Brian Silverman72890c22015-09-19 14:37:37 -040061}
62
Austin Schuh189376f2018-12-20 22:11:15 +110063template <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;
Austin Schuh189376f2018-12-20 22:11:15 +110068 Index rows = A.rows();
69 Scalar avgEival = A.trace() / Scalar(RealScalar(rows));
70 MatrixType Ashifted = A - avgEival * MatrixType::Identity(rows, rows);
71 RealScalar mu = matrix_function_compute_mu(Ashifted);
72 MatrixType F = m_f(avgEival, 0) * MatrixType::Identity(rows, rows);
73 MatrixType P = Ashifted;
74 MatrixType Fincr;
Austin Schuhc55b0172022-02-20 17:52:35 -080075 for (Index s = 1; double(s) < 1.1 * double(rows) + 10.0; s++) { // upper limit is fairly arbitrary
Austin Schuh189376f2018-12-20 22:11:15 +110076 Fincr = m_f(avgEival, static_cast<int>(s)) * P;
77 F += Fincr;
Austin Schuhc55b0172022-02-20 17:52:35 -080078 P = Scalar(RealScalar(1)/RealScalar(s + 1)) * P * Ashifted;
Austin Schuh189376f2018-12-20 22:11:15 +110079
80 // test whether Taylor series converged
81 const RealScalar F_norm = F.cwiseAbs().rowwise().sum().maxCoeff();
82 const RealScalar Fincr_norm = Fincr.cwiseAbs().rowwise().sum().maxCoeff();
83 if (Fincr_norm < NumTraits<Scalar>::epsilon() * F_norm) {
84 RealScalar delta = 0;
85 RealScalar rfactorial = 1;
86 for (Index r = 0; r < rows; r++) {
87 RealScalar mx = 0;
88 for (Index i = 0; i < rows; i++)
89 mx = (std::max)(mx, std::abs(m_f(Ashifted(i, i) + avgEival, static_cast<int>(s+r))));
90 if (r != 0)
91 rfactorial *= RealScalar(r);
92 delta = (std::max)(delta, mx / rfactorial);
93 }
94 const RealScalar P_norm = P.cwiseAbs().rowwise().sum().maxCoeff();
95 if (mu * delta * P_norm < NumTraits<Scalar>::epsilon() * F_norm) // series converged
96 break;
97 }
98 }
99 return F;
100}
101
102/** \brief Find cluster in \p clusters containing some value
103 * \param[in] key Value to find
104 * \returns Iterator to cluster containing \p key, or \c clusters.end() if no cluster in \p m_clusters
105 * contains \p key.
Brian Silverman72890c22015-09-19 14:37:37 -0400106 */
Austin Schuh189376f2018-12-20 22:11:15 +1100107template <typename Index, typename ListOfClusters>
108typename ListOfClusters::iterator matrix_function_find_cluster(Index key, ListOfClusters& clusters)
Brian Silverman72890c22015-09-19 14:37:37 -0400109{
Austin Schuh189376f2018-12-20 22:11:15 +1100110 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())
114 return i;
115 }
116 return clusters.end();
Brian Silverman72890c22015-09-19 14:37:37 -0400117}
118
119/** \brief Partition eigenvalues in clusters of ei'vals close to each other
120 *
Austin Schuh189376f2018-12-20 22:11:15 +1100121 * \param[in] eivals Eigenvalues
122 * \param[out] clusters Resulting partition of eigenvalues
123 *
124 * The partition satisfies the following two properties:
125 * # Any eigenvalue in a certain cluster is at most matrix_function_separation() away from another eigenvalue
126 * in the same cluster.
127 * # The distance between two eigenvalues in different clusters is more than matrix_function_separation().
128 * The implementation follows Algorithm 4.1 in the paper of Davies and Higham.
Brian Silverman72890c22015-09-19 14:37:37 -0400129 */
Austin Schuh189376f2018-12-20 22:11:15 +1100130template <typename EivalsType, typename Cluster>
131void matrix_function_partition_eigenvalues(const EivalsType& eivals, std::list<Cluster>& clusters)
Brian Silverman72890c22015-09-19 14:37:37 -0400132{
Austin Schuh189376f2018-12-20 22:11:15 +1100133 typedef typename EivalsType::RealScalar RealScalar;
134 for (Index i=0; i<eivals.rows(); ++i) {
135 // Find cluster containing i-th ei'val, adding a new cluster if necessary
136 typename std::list<Cluster>::iterator qi = matrix_function_find_cluster(i, clusters);
137 if (qi == clusters.end()) {
Brian Silverman72890c22015-09-19 14:37:37 -0400138 Cluster l;
Austin Schuh189376f2018-12-20 22:11:15 +1100139 l.push_back(i);
140 clusters.push_back(l);
141 qi = clusters.end();
Brian Silverman72890c22015-09-19 14:37:37 -0400142 --qi;
143 }
144
145 // Look for other element to add to the set
Austin Schuh189376f2018-12-20 22:11:15 +1100146 for (Index j=i+1; j<eivals.rows(); ++j) {
147 if (abs(eivals(j) - eivals(i)) <= RealScalar(matrix_function_separation)
148 && std::find(qi->begin(), qi->end(), j) == qi->end()) {
149 typename std::list<Cluster>::iterator qj = matrix_function_find_cluster(j, clusters);
150 if (qj == clusters.end()) {
151 qi->push_back(j);
Brian Silverman72890c22015-09-19 14:37:37 -0400152 } else {
153 qi->insert(qi->end(), qj->begin(), qj->end());
Austin Schuh189376f2018-12-20 22:11:15 +1100154 clusters.erase(qj);
Brian Silverman72890c22015-09-19 14:37:37 -0400155 }
156 }
157 }
158 }
159}
160
Austin Schuh189376f2018-12-20 22:11:15 +1100161/** \brief Compute size of each cluster given a partitioning */
162template <typename ListOfClusters, typename Index>
163void matrix_function_compute_cluster_size(const ListOfClusters& clusters, Matrix<Index, Dynamic, 1>& clusterSize)
Brian Silverman72890c22015-09-19 14:37:37 -0400164{
Austin Schuh189376f2018-12-20 22:11:15 +1100165 const Index numClusters = static_cast<Index>(clusters.size());
166 clusterSize.setZero(numClusters);
167 Index clusterIndex = 0;
168 for (typename ListOfClusters::const_iterator cluster = clusters.begin(); cluster != clusters.end(); ++cluster) {
169 clusterSize[clusterIndex] = cluster->size();
170 ++clusterIndex;
Brian Silverman72890c22015-09-19 14:37:37 -0400171 }
Brian Silverman72890c22015-09-19 14:37:37 -0400172}
173
Austin Schuh189376f2018-12-20 22:11:15 +1100174/** \brief Compute start of each block using clusterSize */
175template <typename VectorType>
176void matrix_function_compute_block_start(const VectorType& clusterSize, VectorType& blockStart)
Brian Silverman72890c22015-09-19 14:37:37 -0400177{
Austin Schuh189376f2018-12-20 22:11:15 +1100178 blockStart.resize(clusterSize.rows());
179 blockStart(0) = 0;
Austin Schuhc55b0172022-02-20 17:52:35 -0800180 for (Index i = 1; i < clusterSize.rows(); i++) {
Austin Schuh189376f2018-12-20 22:11:15 +1100181 blockStart(i) = blockStart(i-1) + clusterSize(i-1);
182 }
183}
Brian Silverman72890c22015-09-19 14:37:37 -0400184
Austin Schuh189376f2018-12-20 22:11:15 +1100185/** \brief Compute mapping of eigenvalue indices to cluster indices */
186template <typename EivalsType, typename ListOfClusters, typename VectorType>
187void matrix_function_compute_map(const EivalsType& eivals, const ListOfClusters& clusters, VectorType& eivalToCluster)
188{
Austin Schuh189376f2018-12-20 22:11:15 +1100189 eivalToCluster.resize(eivals.rows());
Brian Silverman72890c22015-09-19 14:37:37 -0400190 Index clusterIndex = 0;
Austin Schuh189376f2018-12-20 22:11:15 +1100191 for (typename ListOfClusters::const_iterator cluster = clusters.begin(); cluster != clusters.end(); ++cluster) {
192 for (Index i = 0; i < eivals.rows(); ++i) {
193 if (std::find(cluster->begin(), cluster->end(), i) != cluster->end()) {
194 eivalToCluster[i] = clusterIndex;
Brian Silverman72890c22015-09-19 14:37:37 -0400195 }
196 }
197 ++clusterIndex;
198 }
199}
200
Austin Schuh189376f2018-12-20 22:11:15 +1100201/** \brief Compute permutation which groups ei'vals in same cluster together */
202template <typename DynVectorType, typename VectorType>
203void matrix_function_compute_permutation(const DynVectorType& blockStart, const DynVectorType& eivalToCluster, VectorType& permutation)
Brian Silverman72890c22015-09-19 14:37:37 -0400204{
Austin Schuh189376f2018-12-20 22:11:15 +1100205 DynVectorType indexNextEntry = blockStart;
206 permutation.resize(eivalToCluster.rows());
207 for (Index i = 0; i < eivalToCluster.rows(); i++) {
208 Index cluster = eivalToCluster[i];
209 permutation[i] = indexNextEntry[cluster];
Brian Silverman72890c22015-09-19 14:37:37 -0400210 ++indexNextEntry[cluster];
211 }
212}
213
Austin Schuh189376f2018-12-20 22:11:15 +1100214/** \brief Permute Schur decomposition in U and T according to permutation */
215template <typename VectorType, typename MatrixType>
216void matrix_function_permute_schur(VectorType& permutation, MatrixType& U, MatrixType& T)
Brian Silverman72890c22015-09-19 14:37:37 -0400217{
Austin Schuh189376f2018-12-20 22:11:15 +1100218 for (Index i = 0; i < permutation.rows() - 1; i++) {
Brian Silverman72890c22015-09-19 14:37:37 -0400219 Index j;
Austin Schuh189376f2018-12-20 22:11:15 +1100220 for (j = i; j < permutation.rows(); j++) {
221 if (permutation(j) == i) break;
Brian Silverman72890c22015-09-19 14:37:37 -0400222 }
Austin Schuh189376f2018-12-20 22:11:15 +1100223 eigen_assert(permutation(j) == i);
Brian Silverman72890c22015-09-19 14:37:37 -0400224 for (Index k = j-1; k >= i; k--) {
Austin Schuh189376f2018-12-20 22:11:15 +1100225 JacobiRotation<typename MatrixType::Scalar> rotation;
226 rotation.makeGivens(T(k, k+1), T(k+1, k+1) - T(k, k));
227 T.applyOnTheLeft(k, k+1, rotation.adjoint());
228 T.applyOnTheRight(k, k+1, rotation);
229 U.applyOnTheRight(k, k+1, rotation);
230 std::swap(permutation.coeffRef(k), permutation.coeffRef(k+1));
Brian Silverman72890c22015-09-19 14:37:37 -0400231 }
232 }
233}
234
Austin Schuh189376f2018-12-20 22:11:15 +1100235/** \brief Compute block diagonal part of matrix function.
Brian Silverman72890c22015-09-19 14:37:37 -0400236 *
Austin Schuh189376f2018-12-20 22:11:15 +1100237 * This routine computes the matrix function applied to the block diagonal part of \p T (which should be
238 * upper triangular), with the blocking given by \p blockStart and \p clusterSize. The matrix function of
239 * each diagonal block is computed by \p atomic. The off-diagonal parts of \p fT are set to zero.
Brian Silverman72890c22015-09-19 14:37:37 -0400240 */
Austin Schuh189376f2018-12-20 22:11:15 +1100241template <typename MatrixType, typename AtomicType, typename VectorType>
242void matrix_function_compute_block_atomic(const MatrixType& T, AtomicType& atomic, const VectorType& blockStart, const VectorType& clusterSize, MatrixType& fT)
Brian Silverman72890c22015-09-19 14:37:37 -0400243{
Austin Schuh189376f2018-12-20 22:11:15 +1100244 fT.setZero(T.rows(), T.cols());
Austin Schuhc55b0172022-02-20 17:52:35 -0800245 for (Index i = 0; i < clusterSize.rows(); ++i) {
Austin Schuh189376f2018-12-20 22:11:15 +1100246 fT.block(blockStart(i), blockStart(i), clusterSize(i), clusterSize(i))
247 = atomic.compute(T.block(blockStart(i), blockStart(i), clusterSize(i), clusterSize(i)));
Brian Silverman72890c22015-09-19 14:37:37 -0400248 }
249}
250
251/** \brief Solve a triangular Sylvester equation AX + XB = C
252 *
253 * \param[in] A the matrix A; should be square and upper triangular
254 * \param[in] B the matrix B; should be square and upper triangular
255 * \param[in] C the matrix C; should have correct size.
256 *
257 * \returns the solution X.
258 *
Austin Schuh189376f2018-12-20 22:11:15 +1100259 * If A is m-by-m and B is n-by-n, then both C and X are m-by-n. The (i,j)-th component of the Sylvester
260 * equation is
Brian Silverman72890c22015-09-19 14:37:37 -0400261 * \f[
262 * \sum_{k=i}^m A_{ik} X_{kj} + \sum_{k=1}^j X_{ik} B_{kj} = C_{ij}.
263 * \f]
264 * This can be re-arranged to yield:
265 * \f[
266 * X_{ij} = \frac{1}{A_{ii} + B_{jj}} \Bigl( C_{ij}
267 * - \sum_{k=i+1}^m A_{ik} X_{kj} - \sum_{k=1}^{j-1} X_{ik} B_{kj} \Bigr).
268 * \f]
Austin Schuh189376f2018-12-20 22:11:15 +1100269 * It is assumed that A and B are such that the numerator is never zero (otherwise the Sylvester equation
270 * does not have a unique solution). In that case, these equations can be evaluated in the order
271 * \f$ i=m,\ldots,1 \f$ and \f$ j=1,\ldots,n \f$.
Brian Silverman72890c22015-09-19 14:37:37 -0400272 */
Austin Schuh189376f2018-12-20 22:11:15 +1100273template <typename MatrixType>
274MatrixType matrix_function_solve_triangular_sylvester(const MatrixType& A, const MatrixType& B, const MatrixType& C)
Brian Silverman72890c22015-09-19 14:37:37 -0400275{
276 eigen_assert(A.rows() == A.cols());
277 eigen_assert(A.isUpperTriangular());
278 eigen_assert(B.rows() == B.cols());
279 eigen_assert(B.isUpperTriangular());
280 eigen_assert(C.rows() == A.rows());
281 eigen_assert(C.cols() == B.rows());
282
Austin Schuh189376f2018-12-20 22:11:15 +1100283 typedef typename MatrixType::Scalar Scalar;
284
Brian Silverman72890c22015-09-19 14:37:37 -0400285 Index m = A.rows();
286 Index n = B.rows();
Austin Schuh189376f2018-12-20 22:11:15 +1100287 MatrixType X(m, n);
Brian Silverman72890c22015-09-19 14:37:37 -0400288
289 for (Index i = m - 1; i >= 0; --i) {
290 for (Index j = 0; j < n; ++j) {
291
292 // Compute AX = \sum_{k=i+1}^m A_{ik} X_{kj}
293 Scalar AX;
294 if (i == m - 1) {
295 AX = 0;
296 } else {
297 Matrix<Scalar,1,1> AXmatrix = A.row(i).tail(m-1-i) * X.col(j).tail(m-1-i);
298 AX = AXmatrix(0,0);
299 }
300
301 // Compute XB = \sum_{k=1}^{j-1} X_{ik} B_{kj}
302 Scalar XB;
303 if (j == 0) {
304 XB = 0;
305 } else {
306 Matrix<Scalar,1,1> XBmatrix = X.row(i).head(j) * B.col(j).head(j);
307 XB = XBmatrix(0,0);
308 }
309
310 X(i,j) = (C(i,j) - AX - XB) / (A(i,i) + B(j,j));
311 }
312 }
313 return X;
314}
315
Austin Schuh189376f2018-12-20 22:11:15 +1100316/** \brief Compute part of matrix function above block diagonal.
317 *
318 * This routine completes the computation of \p fT, denoting a matrix function applied to the triangular
319 * matrix \p T. It assumes that the block diagonal part of \p fT has already been computed. The part below
320 * the diagonal is zero, because \p T is upper triangular.
321 */
322template <typename MatrixType, typename VectorType>
323void matrix_function_compute_above_diagonal(const MatrixType& T, const VectorType& blockStart, const VectorType& clusterSize, MatrixType& fT)
324{
325 typedef internal::traits<MatrixType> Traits;
326 typedef typename MatrixType::Scalar Scalar;
Austin Schuh189376f2018-12-20 22:11:15 +1100327 static const int Options = MatrixType::Options;
Austin Schuhc55b0172022-02-20 17:52:35 -0800328 typedef Matrix<Scalar, Dynamic, Dynamic, Options, Traits::RowsAtCompileTime, Traits::ColsAtCompileTime> DynMatrixType;
Austin Schuh189376f2018-12-20 22:11:15 +1100329
330 for (Index k = 1; k < clusterSize.rows(); k++) {
331 for (Index i = 0; i < clusterSize.rows() - k; i++) {
332 // compute (i, i+k) block
333 DynMatrixType A = T.block(blockStart(i), blockStart(i), clusterSize(i), clusterSize(i));
334 DynMatrixType B = -T.block(blockStart(i+k), blockStart(i+k), clusterSize(i+k), clusterSize(i+k));
335 DynMatrixType C = fT.block(blockStart(i), blockStart(i), clusterSize(i), clusterSize(i))
336 * T.block(blockStart(i), blockStart(i+k), clusterSize(i), clusterSize(i+k));
337 C -= T.block(blockStart(i), blockStart(i+k), clusterSize(i), clusterSize(i+k))
338 * fT.block(blockStart(i+k), blockStart(i+k), clusterSize(i+k), clusterSize(i+k));
339 for (Index m = i + 1; m < i + k; m++) {
340 C += fT.block(blockStart(i), blockStart(m), clusterSize(i), clusterSize(m))
341 * T.block(blockStart(m), blockStart(i+k), clusterSize(m), clusterSize(i+k));
342 C -= T.block(blockStart(i), blockStart(m), clusterSize(i), clusterSize(m))
343 * fT.block(blockStart(m), blockStart(i+k), clusterSize(m), clusterSize(i+k));
344 }
345 fT.block(blockStart(i), blockStart(i+k), clusterSize(i), clusterSize(i+k))
346 = matrix_function_solve_triangular_sylvester(A, B, C);
347 }
348 }
349}
350
351/** \ingroup MatrixFunctions_Module
352 * \brief Class for computing matrix functions.
353 * \tparam MatrixType type of the argument of the matrix function,
354 * expected to be an instantiation of the Matrix class template.
355 * \tparam AtomicType type for computing matrix function of atomic blocks.
356 * \tparam IsComplex used internally to select correct specialization.
357 *
358 * This class implements the Schur-Parlett algorithm for computing matrix functions. The spectrum of the
359 * matrix is divided in clustered of eigenvalues that lies close together. This class delegates the
360 * computation of the matrix function on every block corresponding to these clusters to an object of type
361 * \p AtomicType and uses these results to compute the matrix function of the whole matrix. The class
362 * \p AtomicType should have a \p compute() member function for computing the matrix function of a block.
363 *
364 * \sa class MatrixFunctionAtomic, class MatrixLogarithmAtomic
365 */
366template <typename MatrixType, int IsComplex = NumTraits<typename internal::traits<MatrixType>::Scalar>::IsComplex>
367struct matrix_function_compute
368{
369 /** \brief Compute the matrix function.
370 *
371 * \param[in] A argument of matrix function, should be a square matrix.
372 * \param[in] atomic class for computing matrix function of atomic blocks.
373 * \param[out] result the function \p f applied to \p A, as
374 * specified in the constructor.
375 *
376 * See MatrixBase::matrixFunction() for details on how this computation
377 * is implemented.
378 */
379 template <typename AtomicType, typename ResultType>
380 static void run(const MatrixType& A, AtomicType& atomic, ResultType &result);
381};
382
383/** \internal \ingroup MatrixFunctions_Module
384 * \brief Partial specialization of MatrixFunction for real matrices
385 *
386 * This converts the real matrix to a complex matrix, compute the matrix function of that matrix, and then
387 * converts the result back to a real matrix.
388 */
389template <typename MatrixType>
390struct matrix_function_compute<MatrixType, 0>
391{
392 template <typename MatA, typename AtomicType, typename ResultType>
393 static void run(const MatA& A, AtomicType& atomic, ResultType &result)
394 {
395 typedef internal::traits<MatrixType> Traits;
396 typedef typename Traits::Scalar Scalar;
397 static const int Rows = Traits::RowsAtCompileTime, Cols = Traits::ColsAtCompileTime;
398 static const int MaxRows = Traits::MaxRowsAtCompileTime, MaxCols = Traits::MaxColsAtCompileTime;
399
400 typedef std::complex<Scalar> ComplexScalar;
401 typedef Matrix<ComplexScalar, Rows, Cols, 0, MaxRows, MaxCols> ComplexMatrix;
402
403 ComplexMatrix CA = A.template cast<ComplexScalar>();
404 ComplexMatrix Cresult;
405 matrix_function_compute<ComplexMatrix>::run(CA, atomic, Cresult);
406 result = Cresult.real();
407 }
408};
409
410/** \internal \ingroup MatrixFunctions_Module
411 * \brief Partial specialization of MatrixFunction for complex matrices
412 */
413template <typename MatrixType>
414struct matrix_function_compute<MatrixType, 1>
415{
416 template <typename MatA, typename AtomicType, typename ResultType>
417 static void run(const MatA& A, AtomicType& atomic, ResultType &result)
418 {
419 typedef internal::traits<MatrixType> Traits;
420
421 // compute Schur decomposition of A
Austin Schuhc55b0172022-02-20 17:52:35 -0800422 const ComplexSchur<MatrixType> schurOfA(A);
423 eigen_assert(schurOfA.info()==Success);
Austin Schuh189376f2018-12-20 22:11:15 +1100424 MatrixType T = schurOfA.matrixT();
425 MatrixType U = schurOfA.matrixU();
426
427 // partition eigenvalues into clusters of ei'vals "close" to each other
428 std::list<std::list<Index> > clusters;
429 matrix_function_partition_eigenvalues(T.diagonal(), clusters);
430
431 // compute size of each cluster
432 Matrix<Index, Dynamic, 1> clusterSize;
433 matrix_function_compute_cluster_size(clusters, clusterSize);
434
435 // blockStart[i] is row index at which block corresponding to i-th cluster starts
436 Matrix<Index, Dynamic, 1> blockStart;
437 matrix_function_compute_block_start(clusterSize, blockStart);
438
439 // compute map so that eivalToCluster[i] = j means that i-th ei'val is in j-th cluster
440 Matrix<Index, Dynamic, 1> eivalToCluster;
441 matrix_function_compute_map(T.diagonal(), clusters, eivalToCluster);
442
443 // compute permutation which groups ei'vals in same cluster together
444 Matrix<Index, Traits::RowsAtCompileTime, 1> permutation;
445 matrix_function_compute_permutation(blockStart, eivalToCluster, permutation);
446
447 // permute Schur decomposition
448 matrix_function_permute_schur(permutation, U, T);
449
450 // compute result
451 MatrixType fT; // matrix function applied to T
452 matrix_function_compute_block_atomic(T, atomic, blockStart, clusterSize, fT);
453 matrix_function_compute_above_diagonal(T, blockStart, clusterSize, fT);
454 result = U * (fT.template triangularView<Upper>() * U.adjoint());
455 }
456};
457
458} // end of namespace internal
459
Brian Silverman72890c22015-09-19 14:37:37 -0400460/** \ingroup MatrixFunctions_Module
461 *
462 * \brief Proxy for the matrix function of some matrix (expression).
463 *
464 * \tparam Derived Type of the argument to the matrix function.
465 *
Austin Schuh189376f2018-12-20 22:11:15 +1100466 * This class holds the argument to the matrix function until it is assigned or evaluated for some other
467 * reason (so the argument should not be changed in the meantime). It is the return type of
468 * matrixBase::matrixFunction() and related functions and most of the time this is the only way it is used.
Brian Silverman72890c22015-09-19 14:37:37 -0400469 */
470template<typename Derived> class MatrixFunctionReturnValue
471: public ReturnByValue<MatrixFunctionReturnValue<Derived> >
472{
473 public:
Brian Silverman72890c22015-09-19 14:37:37 -0400474 typedef typename Derived::Scalar Scalar;
Brian Silverman72890c22015-09-19 14:37:37 -0400475 typedef typename internal::stem_function<Scalar>::type StemFunction;
476
Austin Schuh189376f2018-12-20 22:11:15 +1100477 protected:
478 typedef typename internal::ref_selector<Derived>::type DerivedNested;
479
480 public:
481
482 /** \brief Constructor.
Brian Silverman72890c22015-09-19 14:37:37 -0400483 *
Austin Schuh189376f2018-12-20 22:11:15 +1100484 * \param[in] A %Matrix (expression) forming the argument of the matrix function.
Brian Silverman72890c22015-09-19 14:37:37 -0400485 * \param[in] f Stem function for matrix function under consideration.
486 */
487 MatrixFunctionReturnValue(const Derived& A, StemFunction f) : m_A(A), m_f(f) { }
488
489 /** \brief Compute the matrix function.
490 *
Austin Schuh189376f2018-12-20 22:11:15 +1100491 * \param[out] result \p f applied to \p A, where \p f and \p A are as in the constructor.
Brian Silverman72890c22015-09-19 14:37:37 -0400492 */
493 template <typename ResultType>
494 inline void evalTo(ResultType& result) const
495 {
Austin Schuh189376f2018-12-20 22:11:15 +1100496 typedef typename internal::nested_eval<Derived, 10>::type NestedEvalType;
497 typedef typename internal::remove_all<NestedEvalType>::type NestedEvalTypeClean;
498 typedef internal::traits<NestedEvalTypeClean> Traits;
Brian Silverman72890c22015-09-19 14:37:37 -0400499 typedef std::complex<typename NumTraits<Scalar>::Real> ComplexScalar;
Austin Schuhc55b0172022-02-20 17:52:35 -0800500 typedef Matrix<ComplexScalar, Dynamic, Dynamic, 0, Traits::RowsAtCompileTime, Traits::ColsAtCompileTime> DynMatrixType;
Austin Schuh189376f2018-12-20 22:11:15 +1100501
502 typedef internal::MatrixFunctionAtomic<DynMatrixType> AtomicType;
Brian Silverman72890c22015-09-19 14:37:37 -0400503 AtomicType atomic(m_f);
504
Austin Schuh189376f2018-12-20 22:11:15 +1100505 internal::matrix_function_compute<typename NestedEvalTypeClean::PlainObject>::run(m_A, atomic, result);
Brian Silverman72890c22015-09-19 14:37:37 -0400506 }
507
508 Index rows() const { return m_A.rows(); }
509 Index cols() const { return m_A.cols(); }
510
511 private:
Austin Schuh189376f2018-12-20 22:11:15 +1100512 const DerivedNested m_A;
Brian Silverman72890c22015-09-19 14:37:37 -0400513 StemFunction *m_f;
Brian Silverman72890c22015-09-19 14:37:37 -0400514};
515
516namespace internal {
517template<typename Derived>
518struct traits<MatrixFunctionReturnValue<Derived> >
519{
520 typedef typename Derived::PlainObject ReturnType;
521};
522}
523
524
525/********** MatrixBase methods **********/
526
527
528template <typename Derived>
529const MatrixFunctionReturnValue<Derived> MatrixBase<Derived>::matrixFunction(typename internal::stem_function<typename internal::traits<Derived>::Scalar>::type f) const
530{
531 eigen_assert(rows() == cols());
532 return MatrixFunctionReturnValue<Derived>(derived(), f);
533}
534
535template <typename Derived>
536const MatrixFunctionReturnValue<Derived> MatrixBase<Derived>::sin() const
537{
538 eigen_assert(rows() == cols());
539 typedef typename internal::stem_function<Scalar>::ComplexScalar ComplexScalar;
Austin Schuh189376f2018-12-20 22:11:15 +1100540 return MatrixFunctionReturnValue<Derived>(derived(), internal::stem_function_sin<ComplexScalar>);
Brian Silverman72890c22015-09-19 14:37:37 -0400541}
542
543template <typename Derived>
544const MatrixFunctionReturnValue<Derived> MatrixBase<Derived>::cos() const
545{
546 eigen_assert(rows() == cols());
547 typedef typename internal::stem_function<Scalar>::ComplexScalar ComplexScalar;
Austin Schuh189376f2018-12-20 22:11:15 +1100548 return MatrixFunctionReturnValue<Derived>(derived(), internal::stem_function_cos<ComplexScalar>);
Brian Silverman72890c22015-09-19 14:37:37 -0400549}
550
551template <typename Derived>
552const MatrixFunctionReturnValue<Derived> MatrixBase<Derived>::sinh() const
553{
554 eigen_assert(rows() == cols());
555 typedef typename internal::stem_function<Scalar>::ComplexScalar ComplexScalar;
Austin Schuh189376f2018-12-20 22:11:15 +1100556 return MatrixFunctionReturnValue<Derived>(derived(), internal::stem_function_sinh<ComplexScalar>);
Brian Silverman72890c22015-09-19 14:37:37 -0400557}
558
559template <typename Derived>
560const MatrixFunctionReturnValue<Derived> MatrixBase<Derived>::cosh() const
561{
562 eigen_assert(rows() == cols());
563 typedef typename internal::stem_function<Scalar>::ComplexScalar ComplexScalar;
Austin Schuh189376f2018-12-20 22:11:15 +1100564 return MatrixFunctionReturnValue<Derived>(derived(), internal::stem_function_cosh<ComplexScalar>);
Brian Silverman72890c22015-09-19 14:37:37 -0400565}
566
567} // end namespace Eigen
568
Austin Schuh189376f2018-12-20 22:11:15 +1100569#endif // EIGEN_MATRIX_FUNCTION_H