matrix_function.cpp 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. // This file is part of Eigen, a lightweight C++ template library
  2. // for linear algebra.
  3. //
  4. // Copyright (C) 2010 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. #include "main.h"
  10. #include <unsupported/Eigen/MatrixFunctions>
  11. // Variant of VERIFY_IS_APPROX which uses absolute error instead of
  12. // relative error.
  13. #define VERIFY_IS_APPROX_ABS(a, b) VERIFY(test_isApprox_abs(a, b))
  14. template<typename Type1, typename Type2>
  15. inline bool test_isApprox_abs(const Type1& a, const Type2& b)
  16. {
  17. return ((a-b).array().abs() < test_precision<typename Type1::RealScalar>()).all();
  18. }
  19. // Returns a matrix with eigenvalues clustered around 0, 1 and 2.
  20. template<typename MatrixType>
  21. MatrixType randomMatrixWithRealEivals(const Index size)
  22. {
  23. typedef typename MatrixType::Scalar Scalar;
  24. typedef typename MatrixType::RealScalar RealScalar;
  25. MatrixType diag = MatrixType::Zero(size, size);
  26. for (Index i = 0; i < size; ++i) {
  27. diag(i, i) = Scalar(RealScalar(internal::random<int>(0,2)))
  28. + internal::random<Scalar>() * Scalar(RealScalar(0.01));
  29. }
  30. MatrixType A = MatrixType::Random(size, size);
  31. HouseholderQR<MatrixType> QRofA(A);
  32. return QRofA.householderQ().inverse() * diag * QRofA.householderQ();
  33. }
  34. template <typename MatrixType, int IsComplex = NumTraits<typename internal::traits<MatrixType>::Scalar>::IsComplex>
  35. struct randomMatrixWithImagEivals
  36. {
  37. // Returns a matrix with eigenvalues clustered around 0 and +/- i.
  38. static MatrixType run(const Index size);
  39. };
  40. // Partial specialization for real matrices
  41. template<typename MatrixType>
  42. struct randomMatrixWithImagEivals<MatrixType, 0>
  43. {
  44. static MatrixType run(const Index size)
  45. {
  46. typedef typename MatrixType::Scalar Scalar;
  47. MatrixType diag = MatrixType::Zero(size, size);
  48. Index i = 0;
  49. while (i < size) {
  50. Index randomInt = internal::random<Index>(-1, 1);
  51. if (randomInt == 0 || i == size-1) {
  52. diag(i, i) = internal::random<Scalar>() * Scalar(0.01);
  53. ++i;
  54. } else {
  55. Scalar alpha = Scalar(randomInt) + internal::random<Scalar>() * Scalar(0.01);
  56. diag(i, i+1) = alpha;
  57. diag(i+1, i) = -alpha;
  58. i += 2;
  59. }
  60. }
  61. MatrixType A = MatrixType::Random(size, size);
  62. HouseholderQR<MatrixType> QRofA(A);
  63. return QRofA.householderQ().inverse() * diag * QRofA.householderQ();
  64. }
  65. };
  66. // Partial specialization for complex matrices
  67. template<typename MatrixType>
  68. struct randomMatrixWithImagEivals<MatrixType, 1>
  69. {
  70. static MatrixType run(const Index size)
  71. {
  72. typedef typename MatrixType::Scalar Scalar;
  73. typedef typename MatrixType::RealScalar RealScalar;
  74. const Scalar imagUnit(0, 1);
  75. MatrixType diag = MatrixType::Zero(size, size);
  76. for (Index i = 0; i < size; ++i) {
  77. diag(i, i) = Scalar(RealScalar(internal::random<Index>(-1, 1))) * imagUnit
  78. + internal::random<Scalar>() * Scalar(RealScalar(0.01));
  79. }
  80. MatrixType A = MatrixType::Random(size, size);
  81. HouseholderQR<MatrixType> QRofA(A);
  82. return QRofA.householderQ().inverse() * diag * QRofA.householderQ();
  83. }
  84. };
  85. template<typename MatrixType>
  86. void testMatrixExponential(const MatrixType& A)
  87. {
  88. typedef typename internal::traits<MatrixType>::Scalar Scalar;
  89. typedef typename NumTraits<Scalar>::Real RealScalar;
  90. typedef std::complex<RealScalar> ComplexScalar;
  91. VERIFY_IS_APPROX(A.exp(), A.matrixFunction(internal::stem_function_exp<ComplexScalar>));
  92. }
  93. template<typename MatrixType>
  94. void testMatrixLogarithm(const MatrixType& A)
  95. {
  96. typedef typename internal::traits<MatrixType>::Scalar Scalar;
  97. typedef typename NumTraits<Scalar>::Real RealScalar;
  98. MatrixType scaledA;
  99. RealScalar maxImagPartOfSpectrum = A.eigenvalues().imag().cwiseAbs().maxCoeff();
  100. if (maxImagPartOfSpectrum >= RealScalar(0.9L * EIGEN_PI))
  101. scaledA = A * RealScalar(0.9L * EIGEN_PI) / maxImagPartOfSpectrum;
  102. else
  103. scaledA = A;
  104. // identity X.exp().log() = X only holds if Im(lambda) < pi for all eigenvalues of X
  105. MatrixType expA = scaledA.exp();
  106. MatrixType logExpA = expA.log();
  107. VERIFY_IS_APPROX(logExpA, scaledA);
  108. }
  109. template<typename MatrixType>
  110. void testHyperbolicFunctions(const MatrixType& A)
  111. {
  112. // Need to use absolute error because of possible cancellation when
  113. // adding/subtracting expA and expmA.
  114. VERIFY_IS_APPROX_ABS(A.sinh(), (A.exp() - (-A).exp()) / 2);
  115. VERIFY_IS_APPROX_ABS(A.cosh(), (A.exp() + (-A).exp()) / 2);
  116. }
  117. template<typename MatrixType>
  118. void testGonioFunctions(const MatrixType& A)
  119. {
  120. typedef typename MatrixType::Scalar Scalar;
  121. typedef typename NumTraits<Scalar>::Real RealScalar;
  122. typedef std::complex<RealScalar> ComplexScalar;
  123. typedef Matrix<ComplexScalar, MatrixType::RowsAtCompileTime,
  124. MatrixType::ColsAtCompileTime, MatrixType::Options> ComplexMatrix;
  125. ComplexScalar imagUnit(0,1);
  126. ComplexScalar two(2,0);
  127. ComplexMatrix Ac = A.template cast<ComplexScalar>();
  128. ComplexMatrix exp_iA = (imagUnit * Ac).exp();
  129. ComplexMatrix exp_miA = (-imagUnit * Ac).exp();
  130. ComplexMatrix sinAc = A.sin().template cast<ComplexScalar>();
  131. VERIFY_IS_APPROX_ABS(sinAc, (exp_iA - exp_miA) / (two*imagUnit));
  132. ComplexMatrix cosAc = A.cos().template cast<ComplexScalar>();
  133. VERIFY_IS_APPROX_ABS(cosAc, (exp_iA + exp_miA) / 2);
  134. }
  135. template<typename MatrixType>
  136. void testMatrix(const MatrixType& A)
  137. {
  138. testMatrixExponential(A);
  139. testMatrixLogarithm(A);
  140. testHyperbolicFunctions(A);
  141. testGonioFunctions(A);
  142. }
  143. template<typename MatrixType>
  144. void testMatrixType(const MatrixType& m)
  145. {
  146. // Matrices with clustered eigenvalue lead to different code paths
  147. // in MatrixFunction.h and are thus useful for testing.
  148. const Index size = m.rows();
  149. for (int i = 0; i < g_repeat; i++) {
  150. testMatrix(MatrixType::Random(size, size).eval());
  151. testMatrix(randomMatrixWithRealEivals<MatrixType>(size));
  152. testMatrix(randomMatrixWithImagEivals<MatrixType>::run(size));
  153. }
  154. }
  155. template<typename MatrixType>
  156. void testMapRef(const MatrixType& A)
  157. {
  158. // Test if passing Ref and Map objects is possible
  159. // (Regression test for Bug #1796)
  160. Index size = A.rows();
  161. MatrixType X; X.setRandom(size, size);
  162. MatrixType Y(size,size);
  163. Ref< MatrixType> R(Y);
  164. Ref<const MatrixType> Rc(X);
  165. Map< MatrixType> M(Y.data(), size, size);
  166. Map<const MatrixType> Mc(X.data(), size, size);
  167. X = X*X; // make sure sqrt is possible
  168. Y = X.sqrt();
  169. R = Rc.sqrt();
  170. M = Mc.sqrt();
  171. Y = X.exp();
  172. R = Rc.exp();
  173. M = Mc.exp();
  174. X = Y; // make sure log is possible
  175. Y = X.log();
  176. R = Rc.log();
  177. M = Mc.log();
  178. Y = X.cos() + Rc.cos() + Mc.cos();
  179. Y = X.sin() + Rc.sin() + Mc.sin();
  180. Y = X.cosh() + Rc.cosh() + Mc.cosh();
  181. Y = X.sinh() + Rc.sinh() + Mc.sinh();
  182. }
  183. EIGEN_DECLARE_TEST(matrix_function)
  184. {
  185. CALL_SUBTEST_1(testMatrixType(Matrix<float,1,1>()));
  186. CALL_SUBTEST_2(testMatrixType(Matrix3cf()));
  187. CALL_SUBTEST_3(testMatrixType(MatrixXf(8,8)));
  188. CALL_SUBTEST_4(testMatrixType(Matrix2d()));
  189. CALL_SUBTEST_5(testMatrixType(Matrix<double,5,5,RowMajor>()));
  190. CALL_SUBTEST_6(testMatrixType(Matrix4cd()));
  191. CALL_SUBTEST_7(testMatrixType(MatrixXd(13,13)));
  192. CALL_SUBTEST_1(testMapRef(Matrix<float,1,1>()));
  193. CALL_SUBTEST_2(testMapRef(Matrix3cf()));
  194. CALL_SUBTEST_3(testMapRef(MatrixXf(8,8)));
  195. CALL_SUBTEST_7(testMapRef(MatrixXd(13,13)));
  196. }