EigenvectorTest.php 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. <?php
  2. namespace MathPHP\Tests\LinearAlgebra\Eigen;
  3. use MathPHP\Exception;
  4. use MathPHP\LinearAlgebra\MatrixFactory;
  5. use MathPHP\LinearAlgebra\Eigenvector;
  6. class EigenvectorTest extends \PHPUnit\Framework\TestCase
  7. {
  8. /**
  9. * @test eigenvector using closedFormPolynomialRootMethod returns the expected eigenvalues
  10. * @dataProvider dataProviderForEigenvector
  11. * @param array $A
  12. * @param array $S
  13. */
  14. public function testEigenvectorsUsingClosedFormPolynomialRootMethod(array $A, array $S)
  15. {
  16. // Given
  17. $A = MatrixFactory::create($A);
  18. // When
  19. $eigenvectors = Eigenvector::eigenvectors($A);
  20. // Then
  21. $this->assertEqualsWithDelta($S, $eigenvectors->getMatrix(), 0.0001);
  22. $this->assertEqualsWithDelta($S, $A->eigenvectors()->getMatrix(), 0.0001);
  23. }
  24. /**
  25. * @test eigenvector using closedFormPolynomialRootMethod returns the expected eigenvalues
  26. * @dataProvider dataProviderForEigenvector
  27. * @param array $A
  28. * @param array $S
  29. */
  30. public function testEigenvectorsUsingClosedFormPolynomialRootMethodFromMatrix(array $A, array $S)
  31. {
  32. // Given
  33. $A = MatrixFactory::create($A);
  34. // When
  35. $eigenvectors = $A->eigenvectors();
  36. // Then
  37. $this->assertEqualsWithDelta($S, $eigenvectors->getMatrix(), 0.0001);
  38. }
  39. public function dataProviderForEigenvector(): array
  40. {
  41. return [
  42. [
  43. [
  44. [0, 1],
  45. [-2, -3],
  46. ],
  47. [
  48. [1 / \sqrt(5), \M_SQRT1_2],
  49. [-2 / \sqrt(5), -\M_SQRT1_2],
  50. ]
  51. ],
  52. [
  53. [
  54. [6, -1],
  55. [2, 3],
  56. ],
  57. [
  58. [\M_SQRT1_2, 1 / \sqrt(5)],
  59. [\M_SQRT1_2, 2 / \sqrt(5)],
  60. ]
  61. ],
  62. [
  63. [
  64. [-2, -4, 2],
  65. [-2, 1, 2],
  66. [4, 2, 5],
  67. ],
  68. [
  69. [1 / \sqrt(293), 2 / \sqrt(6), 2 / \sqrt(14)],
  70. [6 / \sqrt(293), 1 / \sqrt(6), -3 / \sqrt(14)],
  71. [16 / \sqrt(293), -1 / \sqrt(6), -1 / \sqrt(14)],
  72. ]
  73. ],
  74. [ // RREF is a zero matrix
  75. [
  76. [1, 0, 0],
  77. [0, 1, 0],
  78. [0, 0, 1],
  79. ],
  80. [
  81. [1, 0, 0],
  82. [0, 1, 0],
  83. [0, 0, 1],
  84. ]
  85. ],
  86. [ // Matrix has duplicate eigenvalues. One vector is on an axis.
  87. [
  88. [2, 0, 1],
  89. [2, 1, 2],
  90. [3, 0, 4],
  91. ],
  92. [
  93. [1 / \sqrt(14), 0, \M_SQRT1_2],
  94. [2 / \sqrt(14), 1, 0],
  95. [3 / \sqrt(14), 0, -1 * \M_SQRT1_2],
  96. ]
  97. ],
  98. [ // Matrix has duplicate eigenvalues. no solution on the axis
  99. [
  100. [2, 2, -3],
  101. [2, 5, -6],
  102. [3, 6, -8],
  103. ],
  104. [
  105. [1 / \sqrt(14), 1 / \M_SQRT3, 5 / \sqrt(42)],
  106. [2 / \sqrt(14), 1 / \M_SQRT3, -4 / \sqrt(42)],
  107. [3 / \sqrt(14), 1 / \M_SQRT3, -1 / \sqrt(42)],
  108. ]
  109. ],
  110. [ // The top row of the rref has a solitary 1 in position 0,0
  111. [
  112. [4, 1, 2],
  113. [0, 0, -2],
  114. [2, 2, 5],
  115. ],
  116. [
  117. [ 5 / \sqrt(65), 1 / 3, 0],
  118. [-2 / \sqrt(65), 2 / 3, -2 / \sqrt(5)],
  119. [6 / \sqrt(65), -2 / 3, 1 / \sqrt(5),],
  120. ]
  121. ],
  122. ];
  123. }
  124. /**
  125. * @test eigenvector can handle numerical precision errors
  126. * @dataProvider dataProviderForPerturbedEigenvalues
  127. * @param array $A
  128. * @param array $E
  129. * @param array $S
  130. */
  131. public function testEigenvectorsPerturbedEigenvalues(array $A, array $E, array $S)
  132. {
  133. // Perturb E
  134. foreach ($E as $i => $component) {
  135. $E[$i] = $component + (random_int(-1, 1) * 10**-12);
  136. }
  137. // Given
  138. $A = MatrixFactory::create($A);
  139. $S = MatrixFactory::create($S);
  140. // When
  141. $eigenvectors = Eigenvector::eigenvectors($A, $E);
  142. // Then
  143. $this->assertEqualsWithDelta($S, $eigenvectors, 0.0001);
  144. }
  145. public function dataProviderForPerturbedEigenvalues(): array
  146. {
  147. return [
  148. [ // Matrix has duplicate eigenvalues. One vector is on an axis.
  149. [
  150. [2, 0, 1],
  151. [2, 1, 2],
  152. [3, 0, 4],
  153. ],
  154. [5, 1, 1],
  155. [
  156. [1 / \sqrt(14), 0, \M_SQRT1_2],
  157. [2 / \sqrt(14), 1, 0],
  158. [3 / \sqrt(14), 0, -1 * \M_SQRT1_2],
  159. ]
  160. ],
  161. [ // Matrix has duplicate eigenvalues. no solution on the axis
  162. [
  163. [2, 2, -3],
  164. [2, 5, -6],
  165. [3, 6, -8],
  166. ],
  167. [-3, 1, 1],
  168. [
  169. [1 / \sqrt(14), 1 / \M_SQRT3, 5 / \sqrt(42)],
  170. [2 / \sqrt(14), 1 / \M_SQRT3, -4 / \sqrt(42)],
  171. [3 / \sqrt(14), 1 / \M_SQRT3, -1 / \sqrt(42)],
  172. ]
  173. ],
  174. ];
  175. }
  176. /**
  177. * @test eigenvectors throws a BadDataException when the matrix is not square
  178. */
  179. public function testEigenvectorMatrixNotCorrectSize()
  180. {
  181. // Given
  182. $A = MatrixFactory::create([[1,2]]);
  183. // Then
  184. $this->expectException(Exception\BadDataException::class);
  185. // When
  186. Eigenvector::eigenvectors($A, [0]);
  187. }
  188. /**
  189. * @test eigenvectors throws a BadDataException when the array of eigenvales is too long or short
  190. * @dataProvider dataProviderForIncorrectNumberOfEigenvectors
  191. * @param array $A
  192. * @param array $B
  193. */
  194. public function testIncorrectNumberOfEigenvectors(array $A, array $B)
  195. {
  196. // Given
  197. $A = MatrixFactory::create($A);
  198. // Then
  199. $this->expectException(Exception\BadDataException::class);
  200. // When
  201. Eigenvector::eigenvectors($A, $B);
  202. }
  203. public function dataProviderForIncorrectNumberOfEigenvectors(): array
  204. {
  205. return [
  206. [
  207. [
  208. [0, 1],
  209. [-2, -3],
  210. ],
  211. [1,2,3],
  212. ],
  213. ];
  214. }
  215. /**
  216. * @test eigenvectors throws a BadDataException when there is an incorrect eigenvalue provided
  217. * @dataProvider dataProviderForEigenvectorNotAnEigenvector
  218. * @param array $A
  219. * @param array $B
  220. */
  221. public function testEigenvectorNotAnEigenvector(array $A, array $B)
  222. {
  223. // Given
  224. $A = MatrixFactory::create($A);
  225. // Then
  226. $this->expectException(Exception\BadDataException::class);
  227. // When
  228. Eigenvector::eigenvectors($A, $B);
  229. }
  230. public function dataProviderForEigenvectorNotAnEigenvector(): array
  231. {
  232. return [
  233. [
  234. [
  235. [0, 1],
  236. [-2, -3],
  237. ],
  238. [-2, 0],
  239. ],
  240. [
  241. [
  242. [0, 1],
  243. [-2, -3],
  244. ],
  245. [0, -3],
  246. ],
  247. ];
  248. }
  249. /**
  250. * @test Matrix eigenvectors throws a MatrixException if the eigenvalue method is not valid
  251. */
  252. public function testMatrixEigenvectorInvalidMethodException()
  253. {
  254. // Given
  255. $A = MatrixFactory::create([
  256. [1, 2, 3],
  257. [2, 3, 4],
  258. [3, 4, 5],
  259. ]);
  260. $invalidMethod = 'SecretMethod';
  261. // Then
  262. $this->expectException(Exception\MatrixException::class);
  263. // When
  264. $A->eigenvectors($invalidMethod);
  265. }
  266. }