source: pacpussensors/trunk/Vislab/lib3dv/eigen/unsupported/Eigen/src/MatrixFunctions/MatrixFunction.h@ 136

Last change on this file since 136 was 136, checked in by ldecherf, 7 years ago

Doc

File size: 21.9 KB
Line 
1// This file is part of Eigen, a lightweight C++ template library
2// for linear algebra.
3//
4// Copyright (C) 2009-2011 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#include "MatrixFunctionAtomic.h"
15
16
17namespace Eigen {
18
19/** \ingroup MatrixFunctions_Module
20 * \brief Class for computing matrix functions.
21 * \tparam MatrixType type of the argument of the matrix function,
22 * expected to be an instantiation of the Matrix class template.
23 * \tparam AtomicType type for computing matrix function of atomic blocks.
24 * \tparam IsComplex used internally to select correct specialization.
25 *
26 * This class implements the Schur-Parlett algorithm for computing matrix functions. The spectrum of the
27 * matrix is divided in clustered of eigenvalues that lies close together. This class delegates the
28 * computation of the matrix function on every block corresponding to these clusters to an object of type
29 * \p AtomicType and uses these results to compute the matrix function of the whole matrix. The class
30 * \p AtomicType should have a \p compute() member function for computing the matrix function of a block.
31 *
32 * \sa class MatrixFunctionAtomic, class MatrixLogarithmAtomic
33 */
34template <typename MatrixType,
35 typename AtomicType,
36 int IsComplex = NumTraits<typename internal::traits<MatrixType>::Scalar>::IsComplex>
37class MatrixFunction
38{
39 public:
40
41 /** \brief Constructor.
42 *
43 * \param[in] A argument of matrix function, should be a square matrix.
44 * \param[in] atomic class for computing matrix function of atomic blocks.
45 *
46 * The class stores references to \p A and \p atomic, so they should not be
47 * changed (or destroyed) before compute() is called.
48 */
49 MatrixFunction(const MatrixType& A, AtomicType& atomic);
50
51 /** \brief Compute the matrix function.
52 *
53 * \param[out] result the function \p f applied to \p A, as
54 * specified in the constructor.
55 *
56 * See MatrixBase::matrixFunction() for details on how this computation
57 * is implemented.
58 */
59 template <typename ResultType>
60 void compute(ResultType &result);
61};
62
63
64/** \internal \ingroup MatrixFunctions_Module
65 * \brief Partial specialization of MatrixFunction for real matrices
66 */
67template <typename MatrixType, typename AtomicType>
68class MatrixFunction<MatrixType, AtomicType, 0>
69{
70 private:
71
72 typedef internal::traits<MatrixType> Traits;
73 typedef typename Traits::Scalar Scalar;
74 static const int Rows = Traits::RowsAtCompileTime;
75 static const int Cols = Traits::ColsAtCompileTime;
76 static const int Options = MatrixType::Options;
77 static const int MaxRows = Traits::MaxRowsAtCompileTime;
78 static const int MaxCols = Traits::MaxColsAtCompileTime;
79
80 typedef std::complex<Scalar> ComplexScalar;
81 typedef Matrix<ComplexScalar, Rows, Cols, Options, MaxRows, MaxCols> ComplexMatrix;
82
83 public:
84
85 /** \brief Constructor.
86 *
87 * \param[in] A argument of matrix function, should be a square matrix.
88 * \param[in] atomic class for computing matrix function of atomic blocks.
89 */
90 MatrixFunction(const MatrixType& A, AtomicType& atomic) : m_A(A), m_atomic(atomic) { }
91
92 /** \brief Compute the matrix function.
93 *
94 * \param[out] result the function \p f applied to \p A, as
95 * specified in the constructor.
96 *
97 * This function converts the real matrix \c A to a complex matrix,
98 * uses MatrixFunction<MatrixType,1> and then converts the result back to
99 * a real matrix.
100 */
101 template <typename ResultType>
102 void compute(ResultType& result)
103 {
104 ComplexMatrix CA = m_A.template cast<ComplexScalar>();
105 ComplexMatrix Cresult;
106 MatrixFunction<ComplexMatrix, AtomicType> mf(CA, m_atomic);
107 mf.compute(Cresult);
108 result = Cresult.real();
109 }
110
111 private:
112 typename internal::nested<MatrixType>::type m_A; /**< \brief Reference to argument of matrix function. */
113 AtomicType& m_atomic; /**< \brief Class for computing matrix function of atomic blocks. */
114
115 MatrixFunction& operator=(const MatrixFunction&);
116};
117
118
119/** \internal \ingroup MatrixFunctions_Module
120 * \brief Partial specialization of MatrixFunction for complex matrices
121 */
122template <typename MatrixType, typename AtomicType>
123class MatrixFunction<MatrixType, AtomicType, 1>
124{
125 private:
126
127 typedef internal::traits<MatrixType> Traits;
128 typedef typename MatrixType::Scalar Scalar;
129 typedef typename MatrixType::Index Index;
130 static const int RowsAtCompileTime = Traits::RowsAtCompileTime;
131 static const int ColsAtCompileTime = Traits::ColsAtCompileTime;
132 static const int Options = MatrixType::Options;
133 typedef typename NumTraits<Scalar>::Real RealScalar;
134 typedef Matrix<Scalar, Traits::RowsAtCompileTime, 1> VectorType;
135 typedef Matrix<Index, Traits::RowsAtCompileTime, 1> IntVectorType;
136 typedef Matrix<Index, Dynamic, 1> DynamicIntVectorType;
137 typedef std::list<Scalar> Cluster;
138 typedef std::list<Cluster> ListOfClusters;
139 typedef Matrix<Scalar, Dynamic, Dynamic, Options, RowsAtCompileTime, ColsAtCompileTime> DynMatrixType;
140
141 public:
142
143 MatrixFunction(const MatrixType& A, AtomicType& atomic);
144 template <typename ResultType> void compute(ResultType& result);
145
146 private:
147
148 void computeSchurDecomposition();
149 void partitionEigenvalues();
150 typename ListOfClusters::iterator findCluster(Scalar key);
151 void computeClusterSize();
152 void computeBlockStart();
153 void constructPermutation();
154 void permuteSchur();
155 void swapEntriesInSchur(Index index);
156 void computeBlockAtomic();
157 Block<MatrixType> block(MatrixType& A, Index i, Index j);
158 void computeOffDiagonal();
159 DynMatrixType solveTriangularSylvester(const DynMatrixType& A, const DynMatrixType& B, const DynMatrixType& C);
160
161 typename internal::nested<MatrixType>::type m_A; /**< \brief Reference to argument of matrix function. */
162 AtomicType& m_atomic; /**< \brief Class for computing matrix function of atomic blocks. */
163 MatrixType m_T; /**< \brief Triangular part of Schur decomposition */
164 MatrixType m_U; /**< \brief Unitary part of Schur decomposition */
165 MatrixType m_fT; /**< \brief %Matrix function applied to #m_T */
166 ListOfClusters m_clusters; /**< \brief Partition of eigenvalues into clusters of ei'vals "close" to each other */
167 DynamicIntVectorType m_eivalToCluster; /**< \brief m_eivalToCluster[i] = j means i-th ei'val is in j-th cluster */
168 DynamicIntVectorType m_clusterSize; /**< \brief Number of eigenvalues in each clusters */
169 DynamicIntVectorType m_blockStart; /**< \brief Row index at which block corresponding to i-th cluster starts */
170 IntVectorType m_permutation; /**< \brief Permutation which groups ei'vals in the same cluster together */
171
172 /** \brief Maximum distance allowed between eigenvalues to be considered "close".
173 *
174 * This is morally a \c static \c const \c Scalar, but only
175 * integers can be static constant class members in C++. The
176 * separation constant is set to 0.1, a value taken from the
177 * paper by Davies and Higham. */
178 static const RealScalar separation() { return static_cast<RealScalar>(0.1); }
179
180 MatrixFunction& operator=(const MatrixFunction&);
181};
182
183/** \brief Constructor.
184 *
185 * \param[in] A argument of matrix function, should be a square matrix.
186 * \param[in] atomic class for computing matrix function of atomic blocks.
187 */
188template <typename MatrixType, typename AtomicType>
189MatrixFunction<MatrixType,AtomicType,1>::MatrixFunction(const MatrixType& A, AtomicType& atomic)
190 : m_A(A), m_atomic(atomic)
191{
192 /* empty body */
193}
194
195/** \brief Compute the matrix function.
196 *
197 * \param[out] result the function \p f applied to \p A, as
198 * specified in the constructor.
199 */
200template <typename MatrixType, typename AtomicType>
201template <typename ResultType>
202void MatrixFunction<MatrixType,AtomicType,1>::compute(ResultType& result)
203{
204 computeSchurDecomposition();
205 partitionEigenvalues();
206 computeClusterSize();
207 computeBlockStart();
208 constructPermutation();
209 permuteSchur();
210 computeBlockAtomic();
211 computeOffDiagonal();
212 result = m_U * (m_fT.template triangularView<Upper>() * m_U.adjoint());
213}
214
215/** \brief Store the Schur decomposition of #m_A in #m_T and #m_U */
216template <typename MatrixType, typename AtomicType>
217void MatrixFunction<MatrixType,AtomicType,1>::computeSchurDecomposition()
218{
219 const ComplexSchur<MatrixType> schurOfA(m_A);
220 m_T = schurOfA.matrixT();
221 m_U = schurOfA.matrixU();
222}
223
224/** \brief Partition eigenvalues in clusters of ei'vals close to each other
225 *
226 * This function computes #m_clusters. This is a partition of the
227 * eigenvalues of #m_T in clusters, such that
228 * # Any eigenvalue in a certain cluster is at most separation() away
229 * from another eigenvalue in the same cluster.
230 * # The distance between two eigenvalues in different clusters is
231 * more than separation().
232 * The implementation follows Algorithm 4.1 in the paper of Davies
233 * and Higham.
234 */
235template <typename MatrixType, typename AtomicType>
236void MatrixFunction<MatrixType,AtomicType,1>::partitionEigenvalues()
237{
238 using std::abs;
239 const Index rows = m_T.rows();
240 VectorType diag = m_T.diagonal(); // contains eigenvalues of A
241
242 for (Index i=0; i<rows; ++i) {
243 // Find set containing diag(i), adding a new set if necessary
244 typename ListOfClusters::iterator qi = findCluster(diag(i));
245 if (qi == m_clusters.end()) {
246 Cluster l;
247 l.push_back(diag(i));
248 m_clusters.push_back(l);
249 qi = m_clusters.end();
250 --qi;
251 }
252
253 // Look for other element to add to the set
254 for (Index j=i+1; j<rows; ++j) {
255 if (abs(diag(j) - diag(i)) <= separation() && std::find(qi->begin(), qi->end(), diag(j)) == qi->end()) {
256 typename ListOfClusters::iterator qj = findCluster(diag(j));
257 if (qj == m_clusters.end()) {
258 qi->push_back(diag(j));
259 } else {
260 qi->insert(qi->end(), qj->begin(), qj->end());
261 m_clusters.erase(qj);
262 }
263 }
264 }
265 }
266}
267
268/** \brief Find cluster in #m_clusters containing some value
269 * \param[in] key Value to find
270 * \returns Iterator to cluster containing \c key, or
271 * \c m_clusters.end() if no cluster in m_clusters contains \c key.
272 */
273template <typename MatrixType, typename AtomicType>
274typename MatrixFunction<MatrixType,AtomicType,1>::ListOfClusters::iterator MatrixFunction<MatrixType,AtomicType,1>::findCluster(Scalar key)
275{
276 typename Cluster::iterator j;
277 for (typename ListOfClusters::iterator i = m_clusters.begin(); i != m_clusters.end(); ++i) {
278 j = std::find(i->begin(), i->end(), key);
279 if (j != i->end())
280 return i;
281 }
282 return m_clusters.end();
283}
284
285/** \brief Compute #m_clusterSize and #m_eivalToCluster using #m_clusters */
286template <typename MatrixType, typename AtomicType>
287void MatrixFunction<MatrixType,AtomicType,1>::computeClusterSize()
288{
289 const Index rows = m_T.rows();
290 VectorType diag = m_T.diagonal();
291 const Index numClusters = static_cast<Index>(m_clusters.size());
292
293 m_clusterSize.setZero(numClusters);
294 m_eivalToCluster.resize(rows);
295 Index clusterIndex = 0;
296 for (typename ListOfClusters::const_iterator cluster = m_clusters.begin(); cluster != m_clusters.end(); ++cluster) {
297 for (Index i = 0; i < diag.rows(); ++i) {
298 if (std::find(cluster->begin(), cluster->end(), diag(i)) != cluster->end()) {
299 ++m_clusterSize[clusterIndex];
300 m_eivalToCluster[i] = clusterIndex;
301 }
302 }
303 ++clusterIndex;
304 }
305}
306
307/** \brief Compute #m_blockStart using #m_clusterSize */
308template <typename MatrixType, typename AtomicType>
309void MatrixFunction<MatrixType,AtomicType,1>::computeBlockStart()
310{
311 m_blockStart.resize(m_clusterSize.rows());
312 m_blockStart(0) = 0;
313 for (Index i = 1; i < m_clusterSize.rows(); i++) {
314 m_blockStart(i) = m_blockStart(i-1) + m_clusterSize(i-1);
315 }
316}
317
318/** \brief Compute #m_permutation using #m_eivalToCluster and #m_blockStart */
319template <typename MatrixType, typename AtomicType>
320void MatrixFunction<MatrixType,AtomicType,1>::constructPermutation()
321{
322 DynamicIntVectorType indexNextEntry = m_blockStart;
323 m_permutation.resize(m_T.rows());
324 for (Index i = 0; i < m_T.rows(); i++) {
325 Index cluster = m_eivalToCluster[i];
326 m_permutation[i] = indexNextEntry[cluster];
327 ++indexNextEntry[cluster];
328 }
329}
330
331/** \brief Permute Schur decomposition in #m_U and #m_T according to #m_permutation */
332template <typename MatrixType, typename AtomicType>
333void MatrixFunction<MatrixType,AtomicType,1>::permuteSchur()
334{
335 IntVectorType p = m_permutation;
336 for (Index i = 0; i < p.rows() - 1; i++) {
337 Index j;
338 for (j = i; j < p.rows(); j++) {
339 if (p(j) == i) break;
340 }
341 eigen_assert(p(j) == i);
342 for (Index k = j-1; k >= i; k--) {
343 swapEntriesInSchur(k);
344 std::swap(p.coeffRef(k), p.coeffRef(k+1));
345 }
346 }
347}
348
349/** \brief Swap rows \a index and \a index+1 in Schur decomposition in #m_U and #m_T */
350template <typename MatrixType, typename AtomicType>
351void MatrixFunction<MatrixType,AtomicType,1>::swapEntriesInSchur(Index index)
352{
353 JacobiRotation<Scalar> rotation;
354 rotation.makeGivens(m_T(index, index+1), m_T(index+1, index+1) - m_T(index, index));
355 m_T.applyOnTheLeft(index, index+1, rotation.adjoint());
356 m_T.applyOnTheRight(index, index+1, rotation);
357 m_U.applyOnTheRight(index, index+1, rotation);
358}
359
360/** \brief Compute block diagonal part of #m_fT.
361 *
362 * This routine computes the matrix function applied to the block diagonal part of #m_T, with the blocking
363 * given by #m_blockStart. The matrix function of each diagonal block is computed by #m_atomic. The
364 * off-diagonal parts of #m_fT are set to zero.
365 */
366template <typename MatrixType, typename AtomicType>
367void MatrixFunction<MatrixType,AtomicType,1>::computeBlockAtomic()
368{
369 m_fT.resize(m_T.rows(), m_T.cols());
370 m_fT.setZero();
371 for (Index i = 0; i < m_clusterSize.rows(); ++i) {
372 block(m_fT, i, i) = m_atomic.compute(block(m_T, i, i));
373 }
374}
375
376/** \brief Return block of matrix according to blocking given by #m_blockStart */
377template <typename MatrixType, typename AtomicType>
378Block<MatrixType> MatrixFunction<MatrixType,AtomicType,1>::block(MatrixType& A, Index i, Index j)
379{
380 return A.block(m_blockStart(i), m_blockStart(j), m_clusterSize(i), m_clusterSize(j));
381}
382
383/** \brief Compute part of #m_fT above block diagonal.
384 *
385 * This routine assumes that the block diagonal part of #m_fT (which
386 * equals the matrix function applied to #m_T) has already been computed and computes
387 * the part above the block diagonal. The part below the diagonal is
388 * zero, because #m_T is upper triangular.
389 */
390template <typename MatrixType, typename AtomicType>
391void MatrixFunction<MatrixType,AtomicType,1>::computeOffDiagonal()
392{
393 for (Index diagIndex = 1; diagIndex < m_clusterSize.rows(); diagIndex++) {
394 for (Index blockIndex = 0; blockIndex < m_clusterSize.rows() - diagIndex; blockIndex++) {
395 // compute (blockIndex, blockIndex+diagIndex) block
396 DynMatrixType A = block(m_T, blockIndex, blockIndex);
397 DynMatrixType B = -block(m_T, blockIndex+diagIndex, blockIndex+diagIndex);
398 DynMatrixType C = block(m_fT, blockIndex, blockIndex) * block(m_T, blockIndex, blockIndex+diagIndex);
399 C -= block(m_T, blockIndex, blockIndex+diagIndex) * block(m_fT, blockIndex+diagIndex, blockIndex+diagIndex);
400 for (Index k = blockIndex + 1; k < blockIndex + diagIndex; k++) {
401 C += block(m_fT, blockIndex, k) * block(m_T, k, blockIndex+diagIndex);
402 C -= block(m_T, blockIndex, k) * block(m_fT, k, blockIndex+diagIndex);
403 }
404 block(m_fT, blockIndex, blockIndex+diagIndex) = solveTriangularSylvester(A, B, C);
405 }
406 }
407}
408
409/** \brief Solve a triangular Sylvester equation AX + XB = C
410 *
411 * \param[in] A the matrix A; should be square and upper triangular
412 * \param[in] B the matrix B; should be square and upper triangular
413 * \param[in] C the matrix C; should have correct size.
414 *
415 * \returns the solution X.
416 *
417 * If A is m-by-m and B is n-by-n, then both C and X are m-by-n.
418 * The (i,j)-th component of the Sylvester equation is
419 * \f[
420 * \sum_{k=i}^m A_{ik} X_{kj} + \sum_{k=1}^j X_{ik} B_{kj} = C_{ij}.
421 * \f]
422 * This can be re-arranged to yield:
423 * \f[
424 * X_{ij} = \frac{1}{A_{ii} + B_{jj}} \Bigl( C_{ij}
425 * - \sum_{k=i+1}^m A_{ik} X_{kj} - \sum_{k=1}^{j-1} X_{ik} B_{kj} \Bigr).
426 * \f]
427 * It is assumed that A and B are such that the numerator is never
428 * zero (otherwise the Sylvester equation does not have a unique
429 * solution). In that case, these equations can be evaluated in the
430 * order \f$ i=m,\ldots,1 \f$ and \f$ j=1,\ldots,n \f$.
431 */
432template <typename MatrixType, typename AtomicType>
433typename MatrixFunction<MatrixType,AtomicType,1>::DynMatrixType MatrixFunction<MatrixType,AtomicType,1>::solveTriangularSylvester(
434 const DynMatrixType& A,
435 const DynMatrixType& B,
436 const DynMatrixType& C)
437{
438 eigen_assert(A.rows() == A.cols());
439 eigen_assert(A.isUpperTriangular());
440 eigen_assert(B.rows() == B.cols());
441 eigen_assert(B.isUpperTriangular());
442 eigen_assert(C.rows() == A.rows());
443 eigen_assert(C.cols() == B.rows());
444
445 Index m = A.rows();
446 Index n = B.rows();
447 DynMatrixType X(m, n);
448
449 for (Index i = m - 1; i >= 0; --i) {
450 for (Index j = 0; j < n; ++j) {
451
452 // Compute AX = \sum_{k=i+1}^m A_{ik} X_{kj}
453 Scalar AX;
454 if (i == m - 1) {
455 AX = 0;
456 } else {
457 Matrix<Scalar,1,1> AXmatrix = A.row(i).tail(m-1-i) * X.col(j).tail(m-1-i);
458 AX = AXmatrix(0,0);
459 }
460
461 // Compute XB = \sum_{k=1}^{j-1} X_{ik} B_{kj}
462 Scalar XB;
463 if (j == 0) {
464 XB = 0;
465 } else {
466 Matrix<Scalar,1,1> XBmatrix = X.row(i).head(j) * B.col(j).head(j);
467 XB = XBmatrix(0,0);
468 }
469
470 X(i,j) = (C(i,j) - AX - XB) / (A(i,i) + B(j,j));
471 }
472 }
473 return X;
474}
475
476/** \ingroup MatrixFunctions_Module
477 *
478 * \brief Proxy for the matrix function of some matrix (expression).
479 *
480 * \tparam Derived Type of the argument to the matrix function.
481 *
482 * This class holds the argument to the matrix function until it is
483 * assigned or evaluated for some other reason (so the argument
484 * should not be changed in the meantime). It is the return type of
485 * matrixBase::matrixFunction() and related functions and most of the
486 * time this is the only way it is used.
487 */
488template<typename Derived> class MatrixFunctionReturnValue
489: public ReturnByValue<MatrixFunctionReturnValue<Derived> >
490{
491 public:
492
493 typedef typename Derived::Scalar Scalar;
494 typedef typename Derived::Index Index;
495 typedef typename internal::stem_function<Scalar>::type StemFunction;
496
497 /** \brief Constructor.
498 *
499 * \param[in] A %Matrix (expression) forming the argument of the
500 * matrix function.
501 * \param[in] f Stem function for matrix function under consideration.
502 */
503 MatrixFunctionReturnValue(const Derived& A, StemFunction f) : m_A(A), m_f(f) { }
504
505 /** \brief Compute the matrix function.
506 *
507 * \param[out] result \p f applied to \p A, where \p f and \p A
508 * are as in the constructor.
509 */
510 template <typename ResultType>
511 inline void evalTo(ResultType& result) const
512 {
513 typedef typename Derived::PlainObject PlainObject;
514 typedef internal::traits<PlainObject> Traits;
515 static const int RowsAtCompileTime = Traits::RowsAtCompileTime;
516 static const int ColsAtCompileTime = Traits::ColsAtCompileTime;
517 static const int Options = PlainObject::Options;
518 typedef std::complex<typename NumTraits<Scalar>::Real> ComplexScalar;
519 typedef Matrix<ComplexScalar, Dynamic, Dynamic, Options, RowsAtCompileTime, ColsAtCompileTime> DynMatrixType;
520 typedef MatrixFunctionAtomic<DynMatrixType> AtomicType;
521 AtomicType atomic(m_f);
522
523 const PlainObject Aevaluated = m_A.eval();
524 MatrixFunction<PlainObject, AtomicType> mf(Aevaluated, atomic);
525 mf.compute(result);
526 }
527
528 Index rows() const { return m_A.rows(); }
529 Index cols() const { return m_A.cols(); }
530
531 private:
532 typename internal::nested<Derived>::type m_A;
533 StemFunction *m_f;
534
535 MatrixFunctionReturnValue& operator=(const MatrixFunctionReturnValue&);
536};
537
538namespace internal {
539template<typename Derived>
540struct traits<MatrixFunctionReturnValue<Derived> >
541{
542 typedef typename Derived::PlainObject ReturnType;
543};
544}
545
546
547/********** MatrixBase methods **********/
548
549
550template <typename Derived>
551const MatrixFunctionReturnValue<Derived> MatrixBase<Derived>::matrixFunction(typename internal::stem_function<typename internal::traits<Derived>::Scalar>::type f) const
552{
553 eigen_assert(rows() == cols());
554 return MatrixFunctionReturnValue<Derived>(derived(), f);
555}
556
557template <typename Derived>
558const MatrixFunctionReturnValue<Derived> MatrixBase<Derived>::sin() const
559{
560 eigen_assert(rows() == cols());
561 typedef typename internal::stem_function<Scalar>::ComplexScalar ComplexScalar;
562 return MatrixFunctionReturnValue<Derived>(derived(), StdStemFunctions<ComplexScalar>::sin);
563}
564
565template <typename Derived>
566const MatrixFunctionReturnValue<Derived> MatrixBase<Derived>::cos() const
567{
568 eigen_assert(rows() == cols());
569 typedef typename internal::stem_function<Scalar>::ComplexScalar ComplexScalar;
570 return MatrixFunctionReturnValue<Derived>(derived(), StdStemFunctions<ComplexScalar>::cos);
571}
572
573template <typename Derived>
574const MatrixFunctionReturnValue<Derived> MatrixBase<Derived>::sinh() const
575{
576 eigen_assert(rows() == cols());
577 typedef typename internal::stem_function<Scalar>::ComplexScalar ComplexScalar;
578 return MatrixFunctionReturnValue<Derived>(derived(), StdStemFunctions<ComplexScalar>::sinh);
579}
580
581template <typename Derived>
582const MatrixFunctionReturnValue<Derived> MatrixBase<Derived>::cosh() const
583{
584 eigen_assert(rows() == cols());
585 typedef typename internal::stem_function<Scalar>::ComplexScalar ComplexScalar;
586 return MatrixFunctionReturnValue<Derived>(derived(), StdStemFunctions<ComplexScalar>::cosh);
587}
588
589} // end namespace Eigen
590
591#endif // EIGEN_MATRIX_FUNCTION
Note: See TracBrowser for help on using the repository browser.