ObjectMatrixTest.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676
  1. <?php
  2. namespace MathPHP\Tests\LinearAlgebra\Matrix\Object;
  3. use MathPHP\Expression\Polynomial;
  4. use MathPHP\LinearAlgebra\MatrixFactory;
  5. use MathPHP\LinearAlgebra\ObjectMatrix;
  6. use MathPHP\LinearAlgebra\Vector;
  7. use MathPHP\Number\ArbitraryInteger;
  8. use MathPHP\Number\Complex;
  9. use MathPHP\Exception;
  10. use MathPHP\Number\ObjectArithmetic;
  11. class ObjectMatrixTest extends \PHPUnit\Framework\TestCase
  12. {
  13. /**
  14. * @test The constructor throws the proper exceptions
  15. * @dataProvider dataProviderConstructorException
  16. * @param array $A
  17. * @param string $exception
  18. */
  19. public function testMatrixConstructorException(array $A, string $exception)
  20. {
  21. // Then
  22. $this->expectException($exception);
  23. // When
  24. $A = new ObjectMatrix($A);
  25. }
  26. public function dataProviderConstructorException(): array
  27. {
  28. return [
  29. 'object does not implement ObjectArithmetic' => [
  30. [[new \stdClass()]],
  31. Exception\IncorrectTypeException::class,
  32. ],
  33. 'multiple objects do not implement ObjectArithmetic' => [
  34. [
  35. [new \stdClass(), new Polynomial([1, 2, 3])],
  36. [new \stdClass(), new Polynomial([1, 2, 3])]
  37. ],
  38. Exception\IncorrectTypeException::class,
  39. ],
  40. 'objects are not the same type' => [
  41. [
  42. [new ArbitraryInteger(5), new Polynomial([1, 2, 3])],
  43. [new ArbitraryInteger(5), new Polynomial([1, 2, 3])]
  44. ],
  45. Exception\IncorrectTypeException::class
  46. ],
  47. 'different row counts' => [
  48. [
  49. [new Polynomial([1, 2, 3]), new Polynomial([1, 2, 3])],
  50. [new Polynomial([1, 2, 3])]
  51. ],
  52. Exception\BadDataException::class
  53. ],
  54. ];
  55. }
  56. /**
  57. * @test Addition throws the proper exceptions
  58. * @dataProvider dataProviderForArithmeticExceptions
  59. * @param array $A
  60. * @param ObjectArithmetic $B
  61. * @param string $exception
  62. */
  63. public function testMatrixAddException(array $A, ObjectArithmetic $B, string $exception)
  64. {
  65. // Given
  66. $A = new ObjectMatrix($A);
  67. // Then
  68. $this->expectException($exception);
  69. // When
  70. $C = $A->add($B);
  71. }
  72. /**
  73. * @test Subtraction throws the proper exceptions
  74. * @dataProvider dataProviderForArithmeticExceptions
  75. * @param array $A
  76. * @param ObjectArithmetic $B
  77. * @param string $exception
  78. */
  79. public function testMatrixSubtractException(array $A, ObjectArithmetic $B, string $exception)
  80. {
  81. // Given
  82. $A = new ObjectMatrix($A);
  83. // Then
  84. $this->expectException($exception);
  85. // When
  86. $C = $A->subtract($B);
  87. }
  88. /**
  89. * @test Multiplication throws the proper exceptions
  90. * @dataProvider dataProviderForArithmeticExceptions
  91. * @param array $A
  92. * @param ObjectArithmetic $B
  93. * @param string $exception
  94. */
  95. public function testMatrixMultiplyException(array $A, ObjectArithmetic $B, string $exception)
  96. {
  97. // Given
  98. $A = new ObjectMatrix($A);
  99. // Then
  100. $this->expectException($exception);
  101. // When
  102. $C = $A->multiply($B);
  103. }
  104. public function dataProviderForArithmeticExceptions(): array
  105. {
  106. return[
  107. [ // Different Sizes
  108. [[new Polynomial([1, 2, 3]), new Polynomial([1, 2, 3])],
  109. [new Polynomial([1, 2, 3]), new Polynomial([1, 2, 3])]],
  110. MatrixFactory::create([[new Polynomial([1, 2, 3])]]),
  111. Exception\MatrixException::class,
  112. ],
  113. [ // Different Types
  114. [[new Polynomial([1, 2, 3])]],
  115. new ObjectMatrix([[new Complex(1, 2)]]),
  116. Exception\IncorrectTypeException::class,
  117. ],
  118. [ // Not a Matrix
  119. [[new Polynomial([1, 2, 3])]],
  120. new Complex(1, 2),
  121. Exception\IncorrectTypeException::class,
  122. ],
  123. ];
  124. }
  125. /**
  126. * @test Cannot compute the determinant of a non-square matrix
  127. * @dataProvider dataProviderDetException
  128. * @param array $A
  129. */
  130. public function testMatrixDetException(array $A)
  131. {
  132. // Given
  133. $A = new ObjectMatrix($A);
  134. // Then
  135. $this->expectException(Exception\MatrixException::class);
  136. // When
  137. $det = $A->det();
  138. }
  139. /**
  140. * @return array
  141. */
  142. public function dataProviderDetException(): array
  143. {
  144. return [
  145. [
  146. [
  147. [new Polynomial([1, 2]), new Polynomial([2, 1])],
  148. ],
  149. ],
  150. ];
  151. }
  152. /**
  153. * @test isEqual
  154. * @dataProvider dataProviderisEqual
  155. * @param array $A
  156. * @param array $B
  157. * @param bool $expected
  158. * @throws \Exception
  159. */
  160. public function testIsEqual(array $A, array $B, bool $expected)
  161. {
  162. // Given
  163. $A = MatrixFactory::create($A);
  164. $B = MatrixFactory::create($B);
  165. // When
  166. $comparison = $A->isEqual($B);
  167. // Then
  168. $this->assertEquals($expected, $comparison);
  169. }
  170. /**
  171. * @return array
  172. */
  173. public function dataProviderisEqual()
  174. {
  175. return [
  176. 'same' => [
  177. [[new Polynomial([1, 0])]],
  178. [[new Polynomial([1, 0])]],
  179. true,
  180. ],
  181. 'different types' => [
  182. [[new Polynomial([1, 0])]],
  183. [[1]],
  184. false,
  185. ],
  186. 'different contents' => [
  187. [[new Polynomial([1, 0])]],
  188. [[new Polynomial([1, 1])]],
  189. false,
  190. ],
  191. 'different shapes' => [
  192. [[new Polynomial([1, 0]), new Polynomial([1, 0])]],
  193. [[new Polynomial([1, 0])], [new Polynomial([1, 0])]],
  194. false,
  195. ],
  196. ];
  197. }
  198. /**
  199. * @test add
  200. * @dataProvider dataProviderAdd
  201. * @param array $A
  202. * @param array $B
  203. * @param array $expected
  204. * @throws \Exception
  205. */
  206. public function testAdd(array $A, array $B, array $expected)
  207. {
  208. // Given
  209. $A = new ObjectMatrix($A);
  210. $B = new ObjectMatrix($B);
  211. // And
  212. $expected = matrixFactory::create($expected);
  213. // When
  214. $sum = $A->add($B);
  215. // Then
  216. $this->assertEquals($expected, $sum);
  217. }
  218. /**
  219. * @return array
  220. */
  221. public function dataProviderAdd(): array
  222. {
  223. return [
  224. [
  225. [
  226. [new Polynomial([1, 0]), new Polynomial([0, 0])],
  227. [new Polynomial([0, 0]), new Polynomial([1, 0])],
  228. ],
  229. [
  230. [new Polynomial([1, 0]), new Polynomial([1, 1])],
  231. [new Polynomial([1, 1]), new Polynomial([1, 0])],
  232. ],
  233. [
  234. [new Polynomial([2, 0]), new Polynomial([1, 1])],
  235. [new Polynomial([1, 1]), new Polynomial([2, 0])],
  236. ],
  237. ],
  238. [
  239. [
  240. [new Polynomial([1, 0]), new Polynomial([1, 0])],
  241. [new Polynomial([1, 0]), new Polynomial([1, 0])],
  242. ],
  243. [
  244. [new Polynomial([1, 0]), new Polynomial([1, 1])],
  245. [new Polynomial([1, 1]), new Polynomial([1, 0])],
  246. ],
  247. [
  248. [new Polynomial([2, 0]), new Polynomial([2, 1])],
  249. [new Polynomial([2, 1]), new Polynomial([2, 0])],
  250. ],
  251. ],
  252. ];
  253. }
  254. /**
  255. * @test subtract
  256. * @dataProvider dataProviderSubtract
  257. * @param array $A
  258. * @param array $B
  259. * @param array $expected
  260. * @throws \Exception
  261. */
  262. public function testSubtract(array $A, array $B, array $expected)
  263. {
  264. // Given
  265. $A = new ObjectMatrix($A);
  266. $B = new ObjectMatrix($B);
  267. $expected = new ObjectMatrix($expected);
  268. // When
  269. $difference = $A->subtract($B);
  270. // Then
  271. $this->assertEquals($expected->getMatrix(), $difference->getMatrix());
  272. }
  273. /**
  274. * @return array
  275. */
  276. public function dataProviderSubtract(): array
  277. {
  278. return [
  279. [
  280. [
  281. [new Polynomial([1, 0]), new Polynomial([0, 0])],
  282. [new Polynomial([0, 0]), new Polynomial([1, 0])],
  283. ],
  284. [
  285. [new Polynomial([2, 1]), new Polynomial([2, 1])],
  286. [new Polynomial([1, -1]), new Polynomial([-1, 0])],
  287. ],
  288. [
  289. [new Polynomial([-1, -1]), new Polynomial([-2, -1])],
  290. [new Polynomial([-1, 1]), new Polynomial([2, 0])],
  291. ],
  292. ],
  293. [
  294. [
  295. [new Polynomial([1, 0]), new Polynomial([1, 0])],
  296. [new Polynomial([1, 0]), new Polynomial([1, 0])],
  297. ],
  298. [
  299. [new Polynomial([-2, 0]), new Polynomial([1, -1])],
  300. [new Polynomial([-2, 2]), new Polynomial([4, 4])],
  301. ],
  302. [
  303. [new Polynomial([3, 0]), new Polynomial([0, 1])],
  304. [new Polynomial([3, -2]), new Polynomial([-3, -4])],
  305. ],
  306. ],
  307. ];
  308. }
  309. /**
  310. * @test multiply
  311. * @dataProvider dataProviderMul
  312. * @param array $A
  313. * @param array $B
  314. * @param array $expected
  315. */
  316. public function testMul(array $A, array $B, array $expected)
  317. {
  318. // Given
  319. $A = new ObjectMatrix($A);
  320. $B = new ObjectMatrix($B);
  321. // And
  322. $expected = matrixFactory::create($expected);
  323. // When
  324. $sum = $A->multiply($B);
  325. // Then
  326. $this->assertEquals($expected, $sum);
  327. }
  328. /**
  329. * @return array
  330. */
  331. public function dataProviderMul(): array
  332. {
  333. return [
  334. [
  335. [
  336. [new Polynomial([1, 0]), new Polynomial([0, 0])],
  337. [new Polynomial([0, 0]), new Polynomial([1, 0])],
  338. ],
  339. [
  340. [new Polynomial([1, 0]), new Polynomial([1, 1])],
  341. [new Polynomial([1, 1]), new Polynomial([1, 0])],
  342. ],
  343. [
  344. [new Polynomial([1, 0, 0]), new Polynomial([1, 1, 0])],
  345. [new Polynomial([1, 1, 0]), new Polynomial([1, 0, 0])],
  346. ],
  347. ],
  348. [
  349. [
  350. [new Polynomial([1, 0]), new Polynomial([1, 0])],
  351. [new Polynomial([1, 0]), new Polynomial([1, 0])],
  352. ],
  353. [
  354. [new Polynomial([1, 0]), new Polynomial([1, 1])],
  355. [new Polynomial([1, 1]), new Polynomial([1, 0])],
  356. ],
  357. [
  358. [new Polynomial([2, 1, 0]), new Polynomial([2, 1, 0])],
  359. [new Polynomial([2, 1, 0]), new Polynomial([2, 1, 0])],
  360. ],
  361. ],
  362. ];
  363. }
  364. /**
  365. * @test Matrix can be multiplied by a vector
  366. * @dataProvider dataProviderMultiplyVector
  367. * @param array $A
  368. * @param array $B
  369. * @param array $expected
  370. */
  371. public function testMultiplyVector(array $A, array $B, array $expected)
  372. {
  373. // Given
  374. $A = new ObjectMatrix($A);
  375. $B = new Vector($B);
  376. // When
  377. $sum = $A->multiply($B);
  378. // Then
  379. $expected = MatrixFactory::create($expected);
  380. $this->assertEquals($expected, $sum);
  381. }
  382. public function dataProviderMultiplyVector(): array
  383. {
  384. return [
  385. [
  386. [
  387. [new Polynomial([1, 0]), new Polynomial([0, 0])],
  388. [new Polynomial([0, 0]), new Polynomial([1, 0])],
  389. ],
  390. [new Polynomial([1, 0]), new Polynomial([1, 1])],
  391. [
  392. [new Polynomial([1, 0, 0])],
  393. [new Polynomial([1, 1, 0])],
  394. ],
  395. ],
  396. ];
  397. }
  398. /**
  399. * @test det
  400. * @dataProvider dataProviderDet
  401. * @param array $A
  402. * @param Polynomial $expected
  403. */
  404. public function testDet(array $A, Polynomial $expected)
  405. {
  406. // Given
  407. $A = new ObjectMatrix($A);
  408. // When
  409. $det = $A->det();
  410. // Then
  411. $this->assertEquals($det, $expected);
  412. // And when
  413. $det = $A->det();
  414. // Then
  415. $this->assertEquals($expected, $det);
  416. }
  417. /**
  418. * @return array
  419. */
  420. public function dataProviderDet(): array
  421. {
  422. return [
  423. [
  424. [
  425. [new Polynomial([1, 0])],
  426. ],
  427. new Polynomial([1, 0]),
  428. ],
  429. [
  430. [
  431. [new Polynomial([1, 0]), new Polynomial([1, 0])],
  432. [new Polynomial([1, 0]), new Polynomial([0, 4])],
  433. ],
  434. new Polynomial([-1, 4, 0]),
  435. ],
  436. ];
  437. }
  438. /**
  439. * @test cofactor
  440. * @dataProvider dataProviderForCofactor
  441. * @param array $A
  442. * @param int $mᵢ
  443. * @param int $nⱼ
  444. * @param ArbitraryInteger $Cᵢⱼ
  445. */
  446. public function testCofactor(array $A, int $mᵢ, int $nⱼ, ArbitraryInteger $Cᵢⱼ)
  447. {
  448. // Given
  449. $A = new ObjectMatrix($A);
  450. // When
  451. $cofactor = $A->cofactor($mᵢ, $nⱼ);
  452. // Then
  453. $this->assertEquals($Cᵢⱼ, $cofactor);
  454. $this->assertEquals($Cᵢⱼ->toInt(), $cofactor->toInt());
  455. }
  456. public function dataProviderForCofactor(): array
  457. {
  458. return [
  459. [
  460. [
  461. [new ArbitraryInteger(1), new ArbitraryInteger(4), new ArbitraryInteger(7)],
  462. [new ArbitraryInteger(3), new ArbitraryInteger(0), new ArbitraryInteger(5)],
  463. [new ArbitraryInteger(-1), new ArbitraryInteger(9), new ArbitraryInteger(11)],
  464. ],
  465. 0, 0, new ArbitraryInteger(-45)
  466. ],
  467. ];
  468. }
  469. /**
  470. * @test transpose
  471. */
  472. public function testTranspose()
  473. {
  474. // Given
  475. $A = [
  476. [new ArbitraryInteger(1), new ArbitraryInteger(4)],
  477. [new ArbitraryInteger(3), new ArbitraryInteger(0)],
  478. ];
  479. $A = new ObjectMatrix($A);
  480. // And
  481. $expected = [
  482. [new ArbitraryInteger(1), new ArbitraryInteger(3)],
  483. [new ArbitraryInteger(4), new ArbitraryInteger(0)],
  484. ];
  485. // When
  486. $Aᵀ = $A->transpose();
  487. // Then
  488. $this->assertEquals($expected, $Aᵀ->getMatrix());
  489. }
  490. /**
  491. * @test scalarMultiply
  492. */
  493. public function testScalarMultiply()
  494. {
  495. // Given
  496. $A = [
  497. [new ArbitraryInteger(1), new ArbitraryInteger(4)],
  498. [new ArbitraryInteger(-3), new ArbitraryInteger(0)],
  499. ];
  500. $A = new ObjectMatrix($A);
  501. // And
  502. $λ = 2;
  503. // When
  504. $λA = $A->scalarMultiply($λ);
  505. // Then
  506. $expected = new ObjectMatrix([
  507. [new ArbitraryInteger(2), new ArbitraryInteger(8)],
  508. [new ArbitraryInteger(-6), new ArbitraryInteger(0)],
  509. ]);
  510. $this->assertEquals($expected->getMatrix(), $λA->getMatrix());
  511. }
  512. /**
  513. * @test scalarMultiply by an object
  514. */
  515. public function testScalarMultiplyByObject()
  516. {
  517. // Given
  518. $A = [
  519. [new ArbitraryInteger(1), new ArbitraryInteger(4)],
  520. [new ArbitraryInteger(-3), new ArbitraryInteger(0)],
  521. ];
  522. $A = new ObjectMatrix($A);
  523. // And
  524. $λ = new ArbitraryInteger(2);
  525. // When
  526. $λA = $A->scalarMultiply($λ);
  527. // Then
  528. $expected = new ObjectMatrix([
  529. [new ArbitraryInteger(2), new ArbitraryInteger(8)],
  530. [new ArbitraryInteger(-6), new ArbitraryInteger(0)],
  531. ]);
  532. $this->assertEquals($expected->getMatrix(), $λA->getMatrix());
  533. }
  534. /**
  535. * @test createZeroValue
  536. */
  537. public function testCreateZeroValue()
  538. {
  539. // Given
  540. $zeroMatrix = ObjectMatrix::createZeroValue();
  541. // And
  542. $expected = [
  543. [new ArbitraryInteger(0)]
  544. ];
  545. // Then
  546. $this->assertEquals($expected, $zeroMatrix->getMatrix());
  547. }
  548. /**
  549. * @test trace
  550. * @dataProvider dataProviderForTrace
  551. * @param array $A
  552. * @param ObjectArithmetic $tr
  553. */
  554. public function testTrace(array $A, ObjectArithmetic $tr)
  555. {
  556. // Given
  557. $A = new ObjectMatrix($A);
  558. // When
  559. $trace = $A->trace();
  560. // Then
  561. $this->assertEquals($tr, $trace);
  562. }
  563. public function dataProviderForTrace(): array
  564. {
  565. return [
  566. [
  567. [
  568. [new ArbitraryInteger(1)]
  569. ],
  570. new ArbitraryInteger(1)
  571. ],
  572. [
  573. [
  574. [new ArbitraryInteger(1), new ArbitraryInteger(2)],
  575. [new ArbitraryInteger(2), new ArbitraryInteger(3)],
  576. ],
  577. new ArbitraryInteger(4)
  578. ],
  579. [
  580. [
  581. [new ArbitraryInteger(1), new ArbitraryInteger(2), new ArbitraryInteger(3)],
  582. [new ArbitraryInteger(4), new ArbitraryInteger(5), new ArbitraryInteger(6)],
  583. [new ArbitraryInteger(7), new ArbitraryInteger(8), new ArbitraryInteger(9)],
  584. ],
  585. new ArbitraryInteger(15)
  586. ],
  587. ];
  588. }
  589. /**
  590. * @test trace error when matrix not square
  591. */
  592. public function testTraceNotSquare()
  593. {
  594. // Given
  595. $A = new ObjectMatrix([
  596. [new ArbitraryInteger(1), new ArbitraryInteger(2)]
  597. ]);
  598. // Then
  599. $this->expectException(Exception\MatrixException::class);
  600. // When
  601. $tr = $A->trace();
  602. }
  603. }