MatrixOperationsTest.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915
  1. <?php
  2. namespace MathPHP\Tests\LinearAlgebra\Matrix\Base;
  3. use MathPHP\Expression\Polynomial;
  4. use MathPHP\LinearAlgebra\MatrixFactory;
  5. use MathPHP\LinearAlgebra\NumericMatrix;
  6. use MathPHP\Exception;
  7. class MatrixOperationsTest extends \PHPUnit\Framework\TestCase
  8. {
  9. /**
  10. * @test transpose
  11. * @dataProvider dataProviderForTranspose
  12. * @param array $A
  13. * @param array $R
  14. * @throws \Exception
  15. */
  16. public function testTranspose(array $A, array $R)
  17. {
  18. // Given
  19. $A = MatrixFactory::create($A);
  20. $R = MatrixFactory::create($R);
  21. // When
  22. $Aᵀ = $A->transpose();
  23. // Then
  24. $this->assertEquals($R->getMatrix(), $Aᵀ->getMatrix());
  25. }
  26. /**
  27. * @test transpose of transpose is the original matrix
  28. * @dataProvider dataProviderForTranspose
  29. * @param array $A
  30. * @throws \Exception
  31. */
  32. public function testTransposeOfTransposeIsOriginalMatrix(array $A)
  33. {
  34. // Given
  35. $A = MatrixFactory::create($A);
  36. // When
  37. $Aᵀ = $A->transpose();
  38. $Aᵀᵀ = $Aᵀ->transpose();
  39. // Then
  40. $this->assertEquals($A->getMatrix(), $Aᵀᵀ->getMatrix());
  41. }
  42. /**
  43. * @return array
  44. */
  45. public function dataProviderForTranspose(): array
  46. {
  47. return [
  48. [
  49. [
  50. [1, 2],
  51. [3, 4],
  52. [5, 6],
  53. ],
  54. [
  55. [1, 3, 5],
  56. [2, 4, 6],
  57. ]
  58. ],
  59. [
  60. [
  61. [5, 4, 3],
  62. [4, 0, 4],
  63. [7, 10, 3],
  64. ],
  65. [
  66. [5, 4, 7],
  67. [4, 0, 10],
  68. [3, 4, 3],
  69. ]
  70. ],
  71. [
  72. [
  73. [5, 4],
  74. [4, 0],
  75. [7, 10],
  76. [-1, 8],
  77. ],
  78. [
  79. [5, 4, 7, -1],
  80. [4, 0, 10, 8],
  81. ]
  82. ]
  83. ];
  84. }
  85. /**
  86. * @test minorMatrix
  87. * @dataProvider dataProviderForMinorMatrix
  88. */
  89. public function testMinorMatrix(array $A, int $mᵢ, int $nⱼ, array $Mᵢⱼ)
  90. {
  91. // Given
  92. $A = MatrixFactory::create($A);
  93. $Mᵢⱼ = MatrixFactory::create($Mᵢⱼ);
  94. // When
  95. $minor = $A->minorMatrix($mᵢ, $nⱼ);
  96. // Then
  97. $this->assertEquals($Mᵢⱼ, $minor);
  98. }
  99. public function dataProviderForMinorMatrix(): array
  100. {
  101. return [
  102. [
  103. [
  104. [1, 4, 7],
  105. [3, 0, 5],
  106. [-1, 9, 11],
  107. ],
  108. 0, 0,
  109. [
  110. [0, 5],
  111. [9, 11],
  112. ],
  113. ],
  114. [
  115. [
  116. [1, 4, 7],
  117. [3, 0, 5],
  118. [-1, 9, 11],
  119. ],
  120. 0, 1,
  121. [
  122. [3, 5],
  123. [-1, 11],
  124. ],
  125. ],
  126. [
  127. [
  128. [1, 4, 7],
  129. [3, 0, 5],
  130. [-1, 9, 11],
  131. ],
  132. 0, 2,
  133. [
  134. [3, 0],
  135. [-1, 9],
  136. ],
  137. ],
  138. [
  139. [
  140. [1, 4, 7],
  141. [3, 0, 5],
  142. [-1, 9, 11],
  143. ],
  144. 1, 0,
  145. [
  146. [4, 7],
  147. [9, 11],
  148. ],
  149. ],
  150. [
  151. [
  152. [1, 4, 7],
  153. [3, 0, 5],
  154. [-1, 9, 11],
  155. ],
  156. 1, 1,
  157. [
  158. [1, 7],
  159. [-1, 11],
  160. ],
  161. ],
  162. [
  163. [
  164. [1, 4, 7],
  165. [3, 0, 5],
  166. [-1, 9, 11],
  167. ],
  168. 1, 2,
  169. [
  170. [1, 4],
  171. [-1, 9],
  172. ],
  173. ],
  174. [
  175. [
  176. [1, 4, 7],
  177. [3, 0, 5],
  178. [-1, 9, 11],
  179. ],
  180. 2, 0,
  181. [
  182. [4, 7],
  183. [0, 5],
  184. ],
  185. ],
  186. [
  187. [
  188. [1, 4, 7],
  189. [3, 0, 5],
  190. [-1, 9, 11],
  191. ],
  192. 2, 1,
  193. [
  194. [1, 7],
  195. [3, 5],
  196. ],
  197. ],
  198. [
  199. [
  200. [1, 4, 7],
  201. [3, 0, 5],
  202. [-1, 9, 11],
  203. ],
  204. 2, 2,
  205. [
  206. [1, 4],
  207. [3, 0],
  208. ],
  209. ],
  210. ];
  211. }
  212. /**
  213. * @test minorMatrix exception - bad row
  214. */
  215. public function testMinorMatrixExceptionBadRow()
  216. {
  217. // Given
  218. $A = MatrixFactory::create([
  219. [1, 2, 3],
  220. [2, 3, 4],
  221. [3, 4, 5],
  222. ]);
  223. // Then
  224. $this->expectException(Exception\MatrixException::class);
  225. // When
  226. $A->minorMatrix(4, 1);
  227. }
  228. /**
  229. * @test minorMatrix exception - bad column
  230. */
  231. public function testMinorMatrixExceptionBadColumn()
  232. {
  233. // Given
  234. $A = MatrixFactory::create([
  235. [1, 2, 3],
  236. [2, 3, 4],
  237. [3, 4, 5],
  238. ]);
  239. // Then
  240. $this->expectException(Exception\MatrixException::class);
  241. // When
  242. $A->minorMatrix(1, 4);
  243. }
  244. /**
  245. * @test minorMatrix exception - not square
  246. */
  247. public function testMinorMatrixExceptionNotSquare()
  248. {
  249. // Given
  250. $A = MatrixFactory::create([
  251. [1, 2, 3, 4],
  252. [2, 3, 4, 4],
  253. [3, 4, 5, 4],
  254. ]);
  255. // Then
  256. $this->expectException(Exception\MatrixException::class);
  257. // When
  258. $A->minorMatrix(1, 1);
  259. }
  260. /**
  261. * @test leadingPrincipalMinor returns the expected SquareMatrix
  262. * @dataProvider dataProviderForLeadingPrincipalMinor
  263. * @param array $A
  264. * @param int $k
  265. * @param array $R
  266. */
  267. public function testLeadingPrincipalMinor(array $A, int $k, array $R)
  268. {
  269. // Given
  270. $A = MatrixFactory::create($A);
  271. $R = MatrixFactory::create($R);
  272. // When
  273. $minor = $A->leadingPrincipalMinor($k);
  274. // Then
  275. $this->assertEquals($R, $minor);
  276. }
  277. public function dataProviderForLeadingPrincipalMinor(): array
  278. {
  279. return [
  280. [
  281. [
  282. [1],
  283. ],
  284. 1,
  285. [
  286. [1],
  287. ],
  288. ],
  289. [
  290. [
  291. [1, 2],
  292. [4, 5],
  293. ],
  294. 1,
  295. [
  296. [1],
  297. ],
  298. ],
  299. [
  300. [
  301. [1, 2],
  302. [4, 5],
  303. ],
  304. 2,
  305. [
  306. [1, 2],
  307. [4, 5],
  308. ],
  309. ],
  310. [
  311. [
  312. [1, 2, 3],
  313. [4, 5, 6],
  314. [7, 8, 9],
  315. ],
  316. 1,
  317. [
  318. [1],
  319. ],
  320. ],
  321. [
  322. [
  323. [1, 2, 3],
  324. [4, 5, 6],
  325. [7, 8, 9],
  326. ],
  327. 2,
  328. [
  329. [1, 2],
  330. [4, 5],
  331. ],
  332. ],
  333. [
  334. [
  335. [1, 2, 3],
  336. [4, 5, 6],
  337. [7, 8, 9],
  338. ],
  339. 3,
  340. [
  341. [1, 2, 3],
  342. [4, 5, 6],
  343. [7, 8, 9],
  344. ],
  345. ],
  346. [
  347. [
  348. [1, 2, 3, 4],
  349. [5, 6, 7, 8],
  350. [9, 0, 1, 2],
  351. [3, 4, 5, 6],
  352. ],
  353. 1,
  354. [
  355. [1],
  356. ],
  357. ],
  358. [
  359. [
  360. [1, 2, 3, 4],
  361. [5, 6, 7, 8],
  362. [9, 0, 1, 2],
  363. [3, 4, 5, 6],
  364. ],
  365. 2,
  366. [
  367. [1, 2],
  368. [5, 6],
  369. ],
  370. ],
  371. [
  372. [
  373. [1, 2, 3, 4],
  374. [5, 6, 7, 8],
  375. [9, 0, 1, 2],
  376. [3, 4, 5, 6],
  377. ],
  378. 3,
  379. [
  380. [1, 2, 3],
  381. [5, 6, 7],
  382. [9, 0, 1],
  383. ],
  384. ],
  385. [
  386. [
  387. [1, 2, 3, 4],
  388. [5, 6, 7, 8],
  389. [9, 0, 1, 2],
  390. [3, 4, 5, 6],
  391. ],
  392. 4,
  393. [
  394. [1, 2, 3, 4],
  395. [5, 6, 7, 8],
  396. [9, 0, 1, 2],
  397. [3, 4, 5, 6],
  398. ],
  399. ],
  400. ];
  401. }
  402. /**
  403. * @test leadingPrincipalMinor throws an OutOfBoundsException when k is < 0.
  404. */
  405. public function testLeadingPrincipalMinorExceptionKLessThanZero()
  406. {
  407. // Given
  408. $A = MatrixFactory::create([
  409. [1, 2, 3],
  410. [2, 3, 4],
  411. [3, 4, 5],
  412. ]);
  413. // Then
  414. $this->expectException(Exception\OutOfBoundsException::class);
  415. // When
  416. $R = $A->leadingPrincipalMinor(-1);
  417. }
  418. /**
  419. * @test leadingPrincipalMinor throws an OutOfBoundsException when k is > n.
  420. */
  421. public function testLeadingPrincipalMinorExceptionKGreaterThanN()
  422. {
  423. // Given
  424. $A = MatrixFactory::create([
  425. [1, 2, 3],
  426. [2, 3, 4],
  427. [3, 4, 5],
  428. ]);
  429. // Then
  430. $this->expectException(Exception\OutOfBoundsException::class);
  431. // When
  432. $R = $A->leadingPrincipalMinor($A->getN() + 1);
  433. }
  434. /**
  435. * @test leadingPrincipalMinor throws a MatrixException if the Matrix is not square.
  436. */
  437. public function testLeadingPrincipalMinorExceptionMatrixNotSquare()
  438. {
  439. // Given
  440. $A = MatrixFactory::create([
  441. [1, 2, 3],
  442. [2, 3, 4],
  443. [3, 4, 5],
  444. [4, 5, 6],
  445. ]);
  446. // Then
  447. $this->expectException(Exception\MatrixException::class);
  448. // When
  449. $R = $A->leadingPrincipalMinor(2);
  450. }
  451. /**
  452. * @test minor
  453. * @dataProvider dataProviderForMinor
  454. */
  455. public function testMinor(array $A, int $mᵢ, int $nⱼ, $Mᵢⱼ)
  456. {
  457. // Given
  458. $A = MatrixFactory::create($A);
  459. // When
  460. $minor = $A->minor($mᵢ, $nⱼ);
  461. // Then
  462. $this->assertEquals($Mᵢⱼ, $minor);
  463. }
  464. public function dataProviderForMinor(): array
  465. {
  466. return [
  467. [
  468. [
  469. [1, 4, 7],
  470. [3, 0, 5],
  471. [-1, 9, 11],
  472. ],
  473. 0, 0, -45
  474. ],
  475. [
  476. [
  477. [1, 4, 7],
  478. [3, 0, 5],
  479. [-1, 9, 11],
  480. ],
  481. 0, 1, 38
  482. ],
  483. [
  484. [
  485. [1, 4, 7],
  486. [3, 0, 5],
  487. [-1, 9, 11],
  488. ],
  489. 1, 2, 13
  490. ],
  491. [
  492. [
  493. [1, 2, 1],
  494. [6, -1, 0],
  495. [-1, -2, -1],
  496. ], 0, 0, 1
  497. ],
  498. [
  499. [
  500. [1, 2, 1],
  501. [6, -1, 0],
  502. [-1, -2, -1],
  503. ], 0, 1, -6
  504. ],
  505. [
  506. [
  507. [1, 2, 1],
  508. [6, -1, 0],
  509. [-1, -2, -1],
  510. ], 0, 2, -13
  511. ],
  512. [
  513. [
  514. [1, 2, 1],
  515. [6, -1, 0],
  516. [-1, -2, -1],
  517. ], 1, 0, 0
  518. ],
  519. [
  520. [
  521. [1, 2, 1],
  522. [6, -1, 0],
  523. [-1, -2, -1],
  524. ], 1, 1, 0
  525. ],
  526. [
  527. [
  528. [1, 2, 1],
  529. [6, -1, 0],
  530. [-1, -2, -1],
  531. ], 1, 2, 0
  532. ],
  533. [
  534. [
  535. [1, 2, 1],
  536. [6, -1, 0],
  537. [-1, -2, -1],
  538. ], 2, 0, 1
  539. ],
  540. [
  541. [
  542. [1, 2, 1],
  543. [6, -1, 0],
  544. [-1, -2, -1],
  545. ], 2, 1, -6
  546. ],
  547. [
  548. [
  549. [1, 2, 1],
  550. [6, -1, 0],
  551. [-1, -2, -1],
  552. ], 2, 2, -13
  553. ],
  554. ];
  555. }
  556. /**
  557. * @test minor exception - bad row
  558. */
  559. public function testMinorExceptionBadRow()
  560. {
  561. // Given
  562. $A = MatrixFactory::create([
  563. [1, 2, 3],
  564. [2, 3, 4],
  565. [3, 4, 5],
  566. ]);
  567. // Then
  568. $this->expectException(Exception\MatrixException::class);
  569. // When
  570. $A->minor(4, 1);
  571. }
  572. /**
  573. * @test minor exception - bad column
  574. */
  575. public function testMinorExceptionBadColumn()
  576. {
  577. // Given
  578. $A = MatrixFactory::create([
  579. [1, 2, 3],
  580. [2, 3, 4],
  581. [3, 4, 5],
  582. ]);
  583. // Then
  584. $this->expectException(Exception\MatrixException::class);
  585. // When
  586. $A->minor(1, 4);
  587. }
  588. /**
  589. * @test minor exception - not square
  590. */
  591. public function testMinorExceptionNotSquare()
  592. {
  593. // Given
  594. $A = MatrixFactory::create([
  595. [1, 2, 3, 4],
  596. [2, 3, 4, 4],
  597. [3, 4, 5, 4],
  598. ]);
  599. // Then
  600. $this->expectException(Exception\MatrixException::class);
  601. // When
  602. $A->minor(1, 1);
  603. }
  604. /**
  605. * @test submatrix
  606. * @dataProvider dataProviderForSubmatrix
  607. * @param array $data
  608. * @param array $params
  609. * @param array $result
  610. * @throws \Exception
  611. */
  612. public function testSubmatrix(array $data, array $params, array $result)
  613. {
  614. // Given
  615. $M = new NumericMatrix($data);
  616. $expectedMatrix = new NumericMatrix($result);
  617. // When
  618. $R = $M->submatrix(...$params);
  619. // Then
  620. $this->assertEquals($expectedMatrix->getMatrix(), $R->getMatrix());
  621. }
  622. /**
  623. * @return array
  624. */
  625. public function dataProviderForSubMatrix(): array
  626. {
  627. return [
  628. [
  629. [
  630. [1, 4, 7],
  631. [3, 0, 5],
  632. [-1, 9, 11],
  633. ],
  634. [1, 1, 2, 2],
  635. [
  636. [0, 5],
  637. [9, 11],
  638. ],
  639. ],
  640. [
  641. [
  642. [1, 4, 7],
  643. [3, 0, 5],
  644. [-1, 9, 11],
  645. ],
  646. [0, 0, 1, 0],
  647. [
  648. [1],
  649. [3],
  650. ],
  651. ],
  652. [
  653. [
  654. [1, 4, 7, 30],
  655. [3, 0, 5, 4],
  656. [-1, 9, 11, 10],
  657. ],
  658. [0, 1, 1, 3],
  659. [
  660. [4, 7, 30],
  661. [0, 5, 4],
  662. ],
  663. ],
  664. ];
  665. }
  666. /**
  667. * @test submatrix exception - bad row
  668. * @throws \Exception
  669. */
  670. public function testSubmatrixExceptionBadRow()
  671. {
  672. // Given
  673. $A = MatrixFactory::create([
  674. [1, 2, 3],
  675. [2, 3, 4],
  676. [3, 4, 5],
  677. ]);
  678. // Then
  679. $this->expectException(Exception\MatrixException::class);
  680. $this->expectExceptionMessage('Specified Matrix row does not exist');
  681. // When
  682. $A->submatrix(0, 0, 4, 1);
  683. }
  684. /**
  685. * @test submatrix exception - bad column
  686. * @throws \Exception
  687. */
  688. public function testSubMatrixExceptionBadColumn()
  689. {
  690. // Given
  691. $A = MatrixFactory::create([
  692. [1, 2, 3],
  693. [2, 3, 4],
  694. [3, 4, 5],
  695. ]);
  696. // Then
  697. $this->expectException(Exception\MatrixException::class);
  698. $this->expectExceptionMessage('Specified Matrix column does not exist');
  699. // When
  700. $A->submatrix(0, 0, 1, 4);
  701. }
  702. /**
  703. * @test submatrix exception - wrong row order
  704. * @throws \Exception
  705. */
  706. public function testSubMatrixWrongRowOrder()
  707. {
  708. // Given
  709. $A = MatrixFactory::create([
  710. [1, 2, 3],
  711. [2, 3, 4],
  712. [3, 4, 5],
  713. ]);
  714. // Then
  715. $this->expectException(Exception\MatrixException::class);
  716. $this->expectExceptionMessage('Ending row must be greater than beginning row');
  717. // When
  718. $A->submatrix(2, 0, 1, 2);
  719. }
  720. /**
  721. * @test submatrix exception - wrong column order
  722. * @throws \Exception
  723. */
  724. public function testSubMatrixWrongColumnOrder()
  725. {
  726. // Given
  727. $A = MatrixFactory::create([
  728. [1, 2, 3],
  729. [2, 3, 4],
  730. [3, 4, 5],
  731. ]);
  732. // Then
  733. $this->expectException(Exception\MatrixException::class);
  734. $this->expectExceptionMessage('Ending column must be greater than the beginning column');
  735. // When
  736. $A->submatrix(0, 2, 1, 0);
  737. }
  738. /**
  739. * @test insert returns the expected value
  740. * @dataProvider dataProviderForInsert
  741. */
  742. public function testInsert(array $A, array $B, int $m, int $n, $expected)
  743. {
  744. // Given
  745. $A = MatrixFactory::create($A);
  746. $B = MatrixFactory::create($B);
  747. // When
  748. $matrixWithInsertion = $A->insert($B, $m, $n);
  749. // Then
  750. $this->assertEquals($expected, $matrixWithInsertion->getMatrix());
  751. }
  752. public function dataProviderForInsert(): array
  753. {
  754. return [
  755. [
  756. [
  757. [1, 1, 1],
  758. [1, 1, 1],
  759. [1, 1, 1],
  760. ],
  761. [
  762. [0, 0],
  763. [0, 0],
  764. ],
  765. 1,
  766. 1,
  767. [
  768. [1, 1, 1],
  769. [1, 0, 0],
  770. [1, 0, 0],
  771. ],
  772. ],
  773. [
  774. [
  775. [1, 1, 1],
  776. [1, 1, 1],
  777. [1, 1, 1],
  778. ],
  779. [
  780. [0, 0],
  781. ],
  782. 1,
  783. 1,
  784. [
  785. [1, 1, 1],
  786. [1, 0, 0],
  787. [1, 1, 1],
  788. ],
  789. ],
  790. [
  791. [
  792. [1, 1, 1],
  793. [1, 1, 1],
  794. [1, 1, 1],
  795. ],
  796. [
  797. [0, 0],
  798. ],
  799. 2,
  800. 1,
  801. [
  802. [1, 1, 1],
  803. [1, 1, 1],
  804. [1, 0, 0],
  805. ],
  806. ],
  807. ];
  808. }
  809. /**
  810. * @test insert exception - Inner matrix exceeds bounds
  811. * @throws \Exception
  812. */
  813. public function testInsertMatrixExceedsBounds()
  814. {
  815. // Given
  816. $A = MatrixFactory::create([
  817. [1, 1, 1],
  818. [1, 1, 1],
  819. [1, 1, 1],
  820. ]);
  821. // And
  822. $B = MatrixFactory::create([
  823. [0, 0, 0],
  824. ]);
  825. // Then
  826. $this->expectException(Exception\MatrixException::class);
  827. // When
  828. $A->insert($B, 1, 1);
  829. }
  830. /**
  831. * @test insert exception - matrix is not the same type.
  832. * @throws \Exception
  833. */
  834. public function testInsertExceptionTypeMismatch()
  835. {
  836. // Given
  837. $A = MatrixFactory::create([[1]]);
  838. $B = MatrixFactory::create([[new Polynomial([1,1])]]);
  839. // Then
  840. $this->expectException(Exception\MatrixException::class);
  841. // When
  842. $A->insert($B, 0, 0);
  843. }
  844. }