MatrixOperationsTest.php 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488
  1. <?php
  2. namespace MathPHP\Tests\LinearAlgebra\Matrix\Numeric;
  3. use MathPHP\LinearAlgebra\MatrixFactory;
  4. use MathPHP\LinearAlgebra\NumericMatrix;
  5. use MathPHP\LinearAlgebra\NumericSquareMatrix;
  6. use MathPHP\Exception;
  7. class MatrixOperationsTest extends \PHPUnit\Framework\TestCase
  8. {
  9. /**
  10. * @test trace
  11. * @dataProvider dataProviderForTrace
  12. */
  13. public function testTrace(array $A, $tr)
  14. {
  15. // Given
  16. $A = MatrixFactory::create($A);
  17. // When
  18. $trace = $A->trace();
  19. // Then
  20. $this->assertEquals($tr, $trace);
  21. }
  22. public function dataProviderForTrace(): array
  23. {
  24. return [
  25. [
  26. [[1]], 1
  27. ],
  28. [
  29. [
  30. [1, 2],
  31. [2, 3],
  32. ], 4
  33. ],
  34. [
  35. [
  36. [1, 2, 3],
  37. [4, 5, 6],
  38. [7, 8, 9],
  39. ], 15
  40. ],
  41. ];
  42. }
  43. /**
  44. * @test trace exception - not square
  45. */
  46. public function testTraceExceptionNotSquareMatrix()
  47. {
  48. // Given
  49. $A = MatrixFactory::create([
  50. [1, 2],
  51. [2, 3],
  52. [3, 4],
  53. ]);
  54. // Then
  55. $this->expectException(Exception\MatrixException::class);
  56. // When
  57. $A->trace();
  58. }
  59. /**
  60. * @test diagonal
  61. * @dataProvider dataProviderForDiagonal
  62. */
  63. public function testDiagonal(array $A, array $R)
  64. {
  65. // Given
  66. $A = MatrixFactory::create($A);
  67. $R = MatrixFactory::create($R);
  68. // When
  69. $diagonal = $A->diagonal();
  70. // Then
  71. $this->assertEquals($R, $diagonal);
  72. }
  73. public function dataProviderForDiagonal(): array
  74. {
  75. return [
  76. [
  77. [
  78. [1, 2, 3],
  79. [2, 3, 4],
  80. [3, 4, 5],
  81. ],
  82. [
  83. [1, 0, 0],
  84. [0, 3, 0],
  85. [0, 0, 5],
  86. ]
  87. ],
  88. [
  89. [
  90. [1, 2, 3],
  91. [2, 3, 4],
  92. [3, 4, 5],
  93. [4, 5, 6],
  94. ],
  95. [
  96. [1, 0, 0],
  97. [0, 3, 0],
  98. [0, 0, 5],
  99. [0, 0, 0],
  100. ]
  101. ],
  102. [
  103. [
  104. [1, 2, 3, 4],
  105. [2, 3, 4, 5],
  106. [3, 4, 5, 6],
  107. ],
  108. [
  109. [1, 0, 0, 0],
  110. [0, 3, 0, 0],
  111. [0, 0, 5, 0],
  112. ]
  113. ],
  114. ];
  115. }
  116. /**
  117. * @test inverse
  118. * @dataProvider dataProviderForInverse
  119. * @param array $A
  120. * @param array $A⁻¹
  121. * @throws \Exception
  122. */
  123. public function testInverse(array $A, array $A⁻¹)
  124. {
  125. // Given
  126. $A = MatrixFactory::create($A);
  127. $A⁻¹ = MatrixFactory::create($A⁻¹);
  128. // When
  129. $inverse = $A->inverse();
  130. $inverseAgain = $A->inverse();
  131. // Then
  132. $this->assertEqualsWithDelta($A⁻¹, $inverse, 0.001); // Test calculation
  133. $this->assertEqualsWithDelta($A⁻¹, $inverseAgain, 0.001); // Test class attribute
  134. }
  135. /**
  136. * @return array
  137. */
  138. public function dataProviderForInverse(): array
  139. {
  140. return [
  141. [
  142. [
  143. [1]
  144. ],
  145. [
  146. [1]
  147. ]
  148. ],
  149. [
  150. [
  151. [2]
  152. ],
  153. [
  154. [1 / 2]
  155. ]
  156. ],
  157. [
  158. [
  159. [10]
  160. ],
  161. [
  162. [1 / 10]
  163. ]
  164. ],
  165. [
  166. [
  167. [-3]
  168. ],
  169. [
  170. [-1 / 3]
  171. ]
  172. ],
  173. [
  174. [
  175. [4, 7],
  176. [2, 6],
  177. ],
  178. [
  179. [0.6, -0.7],
  180. [-0.2, 0.4],
  181. ],
  182. ],
  183. [
  184. [
  185. [4, 3],
  186. [3, 2],
  187. ],
  188. [
  189. [-2, 3],
  190. [3, -4],
  191. ],
  192. ],
  193. [
  194. [
  195. [1, 2],
  196. [3, 4],
  197. ],
  198. [
  199. [-2, 1],
  200. [3 / 2, -1 / 2],
  201. ],
  202. ],
  203. [
  204. [
  205. [3, 3.5],
  206. [3.2, 3.6],
  207. ],
  208. [
  209. [-9, 8.75],
  210. [8, -7.5],
  211. ],
  212. ],
  213. [
  214. [
  215. [1, 2, 3],
  216. [0, 4, 5],
  217. [1, 0, 6],
  218. ],
  219. [
  220. [12 / 11, -6 / 11, -1 / 11],
  221. [5 / 22, 3 / 22, -5 / 22],
  222. [-2 / 11, 1 / 11, 2 / 11],
  223. ],
  224. ],
  225. [
  226. [
  227. [7, 2, 1],
  228. [0, 3, -1],
  229. [-3, 4, -2],
  230. ],
  231. [
  232. [-2, 8, -5],
  233. [3, -11, 7],
  234. [9, -34, 21],
  235. ],
  236. ],
  237. [
  238. [
  239. [2, 0, 0],
  240. [0, 2, 0],
  241. [0, 0, 2],
  242. ],
  243. [
  244. [1/2, 0, 0],
  245. [0, 1/2, 0],
  246. [0, 0, 1/2],
  247. ],
  248. ],
  249. [
  250. [
  251. [3, 6, 6, 8],
  252. [4, 5, 3, 2],
  253. [2, 2, 2, 3],
  254. [6, 8, 4, 2],
  255. ],
  256. [
  257. [-0.333, 0.667, 0.667, -0.333],
  258. [0.167, -2.333, 0.167, 1.417],
  259. [0.167, 4.667, -1.833, -2.583],
  260. [0.000, -2.000, 1.000, 1.000],
  261. ],
  262. ],
  263. [
  264. [
  265. [2, 0, 0, 0],
  266. [0, 2, 0, 0],
  267. [0, 0, 2, 0],
  268. [0, 0, 0, 2],
  269. ],
  270. [
  271. [1/2, 0, 0, 0],
  272. [0, 1/2, 0, 0],
  273. [0, 0, 1/2, 0],
  274. [0, 0, 0, 1/2],
  275. ],
  276. ],
  277. [
  278. [
  279. [4, 23, 6, 4, 7],
  280. [3, 64, 23, 52, 2],
  281. [65, 45, 3, 23, 1],
  282. [2, 3, 4, 3, 9],
  283. [53, 99, 54, 32, 105],
  284. ],
  285. [
  286. [-0.142, 0.006, 0.003, -0.338, 0.038],
  287. [0.172, -0.012, 0.010, 0.275, -0.035],
  288. [-0.856, 0.082, -0.089, -2.344, 0.257],
  289. [0.164, -0.001, 0.026, 0.683, -0.070],
  290. [0.300, -0.033, 0.027, 0.909, -0.088],
  291. ],
  292. ],
  293. ];
  294. }
  295. /**
  296. * @test inverse exception - not square
  297. * @dataProvider dataProviderForInverseExceptionNotSquare
  298. */
  299. public function testInverseExceptionNotSquare(array $A)
  300. {
  301. // Given
  302. $A = MatrixFactory::create($A);
  303. // Then
  304. $this->expectException(Exception\MatrixException::class);
  305. // When
  306. $A->inverse();
  307. }
  308. public function dataProviderForInverseExceptionNotSquare(): array
  309. {
  310. return [
  311. [
  312. [
  313. [3, 4, 4],
  314. [6, 8, 5],
  315. ]
  316. ],
  317. ];
  318. }
  319. /**
  320. * @test inverse exception - det is zero
  321. * @dataProvider dataProviderForInverseExceptionDetIsZero
  322. */
  323. public function testInverseExceptionDetIsZero(array $A)
  324. {
  325. // Given
  326. $A = MatrixFactory::create($A);
  327. // Then
  328. $this->expectException(Exception\MatrixException::class);
  329. // When
  330. $A->inverse();
  331. }
  332. public function dataProviderForInverseExceptionDetIsZero(): array
  333. {
  334. return [
  335. [
  336. [
  337. [3, 4],
  338. [6, 8],
  339. ]
  340. ],
  341. ];
  342. }
  343. /**
  344. * @test cofactor
  345. * @dataProvider dataProviderForCofactor
  346. */
  347. public function testCofactor(array $A, int $mᵢ, int $nⱼ, $Cᵢⱼ)
  348. {
  349. // Given
  350. $A = MatrixFactory::create($A);
  351. // When
  352. $cofactor = $A->cofactor($mᵢ, $nⱼ);
  353. // Then
  354. $this->assertEquals($Cᵢⱼ, $cofactor);
  355. }
  356. public function dataProviderForCofactor(): array
  357. {
  358. return [
  359. [
  360. [
  361. [1, 4, 7],
  362. [3, 0, 5],
  363. [-1, 9, 11],
  364. ],
  365. 0, 0, -45
  366. ],
  367. [
  368. [
  369. [1, 4, 7],
  370. [3, 0, 5],
  371. [-1, 9, 11],
  372. ],
  373. 0, 1, -38
  374. ],
  375. [
  376. [
  377. [1, 4, 7],
  378. [3, 0, 5],
  379. [-1, 9, 11],
  380. ],
  381. 1, 2, -13
  382. ],
  383. [
  384. [
  385. [1, 2, 1],
  386. [6, -1, 0],
  387. [-1, -2, -1],
  388. ], 0, 0, 1
  389. ],
  390. [
  391. [
  392. [1, 2, 1],
  393. [6, -1, 0],
  394. [-1, -2, -1],
  395. ], 0, 1, 6
  396. ],
  397. [
  398. [
  399. [1, 2, 1],
  400. [6, -1, 0],
  401. [-1, -2, -1],
  402. ], 0, 2, -13
  403. ],
  404. [
  405. [
  406. [1, 2, 1],
  407. [6, -1, 0],
  408. [-1, -2, -1],
  409. ], 1, 0, 0
  410. ],
  411. [
  412. [
  413. [1, 2, 1],
  414. [6, -1, 0],
  415. [-1, -2, -1],
  416. ], 1, 1, 0
  417. ],
  418. [
  419. [
  420. [1, 2, 1],
  421. [6, -1, 0],
  422. [-1, -2, -1],
  423. ], 1, 2, 0
  424. ],
  425. [
  426. [
  427. [1, 2, 1],
  428. [6, -1, 0],
  429. [-1, -2, -1],
  430. ], 2, 0, 1
  431. ],
  432. [
  433. [
  434. [1, 2, 1],
  435. [6, -1, 0],
  436. [-1, -2, -1],
  437. ], 0, 0, 1
  438. ],
  439. [
  440. [
  441. [1, 2, 1],
  442. [6, -1, 0],
  443. [-1, -2, -1],
  444. ], 2, 2, -13
  445. ],
  446. ];
  447. }
  448. /**
  449. * @test cofactor exception - bad row
  450. */
  451. public function testCofactorExceptionBadRow()
  452. {
  453. // Given
  454. $A = MatrixFactory::create([
  455. [1, 2, 3],
  456. [2, 3, 4],
  457. [3, 4, 5],
  458. ]);
  459. // Then
  460. $this->expectException(Exception\MatrixException::class);
  461. // When
  462. $A->cofactor(4, 1);
  463. }
  464. /**
  465. * @test cofactor exception - bad column
  466. */
  467. public function testCofactorExceptionBadColumn()
  468. {
  469. $A = MatrixFactory::create([
  470. [1, 2, 3],
  471. [2, 3, 4],
  472. [3, 4, 5],
  473. ]);
  474. $this->expectException(Exception\MatrixException::class);
  475. $A->cofactor(1, 4);
  476. }
  477. /**
  478. * @test cofactor exception - not square
  479. */
  480. public function testCofactorExceptionNotSquare()
  481. {
  482. // Given
  483. $A = MatrixFactory::create([
  484. [1, 2, 3, 4],
  485. [2, 3, 4, 4],
  486. [3, 4, 5, 4],
  487. ]);
  488. // Then
  489. $this->expectException(Exception\MatrixException::class);
  490. // When
  491. $A->cofactor(1, 1);
  492. }
  493. /**
  494. * @test cofactorMatrix
  495. * @dataProvider dataProviderForCofactorMatrix
  496. */
  497. public function testCofactorMatrix(array $A, array $R)
  498. {
  499. // Given
  500. $A = MatrixFactory::create($A);
  501. $R = new NumericSquareMatrix($R);
  502. // When
  503. $cofactor = $A->cofactorMatrix();
  504. // Then
  505. $this->assertEqualsWithDelta($R, $cofactor, 0.00000001);
  506. }
  507. public function dataProviderForCofactorMatrix(): array
  508. {
  509. return [
  510. [
  511. [
  512. [6, 4],
  513. [3, 2],
  514. ],
  515. [
  516. [2, -3],
  517. [-4, 6],
  518. ],
  519. ],
  520. [
  521. [
  522. [1, 2, 3],
  523. [0, 4, 5],
  524. [1, 0, 6],
  525. ],
  526. [
  527. [24, 5, -4],
  528. [-12, 3, 2],
  529. [-2, -5, 4],
  530. ],
  531. ],
  532. [
  533. [
  534. [-1, 2, 3],
  535. [1, 5, 6],
  536. [0, 4, 3],
  537. ],
  538. [
  539. [-9, -3, 4],
  540. [6, -3, 4],
  541. [-3, 9, -7],
  542. ],
  543. ],
  544. [
  545. [
  546. [3, 65, 23],
  547. [98, 35, 86],
  548. [5, 2, 10],
  549. ],
  550. [
  551. [178, -550, 21],
  552. [-604, -85, 319],
  553. [4785, 1996, -6265],
  554. ],
  555. ],
  556. [
  557. [
  558. [2, -1, 4, 3, 2, 3, 3, 4, 4],
  559. [-1, 2, 3, 2, 1, 2, 2, 3, 3],
  560. [4, 3, 2, 1, 2, 3, 3, 4, 4],
  561. [2, 1, 2, 1, 2, 1, 1, 2, 2],
  562. [3, 2, 1, 2, 1, 2, 2, 3, 3],
  563. [3, 2, 3, 2, 1, 2, 2, 3, 3],
  564. [3, 2, 3, 2, 1, 2, 2, 1, 2],
  565. [4, 3, 4, 3, 2, 3, 1, 2, 2],
  566. [4, 3, 4, 3, 2, 3, 2, 2, 2],
  567. ],
  568. [
  569. [0, 128, 0, 0, 0, -128, 0, 0, 0,],
  570. [128, -80, 0, -32, -32, -16, 0, 32, -64,],
  571. [0, 0, 0, 256, 0, -256, 0, 0, 0,],
  572. [0, -32, 0, -64, -320, 352, 0, 64, -128,],
  573. [0, -32, 256, -320, -64, 96, 0, 64, -128,],
  574. [-128, -16, -256, 96, 352, 304, 0, -352, 192,],
  575. [0, 0, 0, 0, -0, 0, 0, 512, -512,],
  576. [-0, 32, 0, 64, 64, -352, 512, 192, -384,],
  577. [0, -64, 0, -128, -128, 192, -512, -384, 768,],
  578. ],
  579. ],
  580. [
  581. [
  582. [0, 1, 4, 3, 2, 3, 3, 4, 4],
  583. [1, 0, 3, 2, 1, 2, 2, 3, 3],
  584. [4, 3, 0, 1, 2, 3, 3, 4, 4],
  585. [3, 2, 1, 0, 1, 2, 2, 3, 3],
  586. [2, 1, 2, 1, 0, 1, 1, 2, 2],
  587. [3, 2, 3, 2, 1, 0, 2, 3, 3],
  588. [3, 2, 3, 2, 1, 2, 0, 1, 2],
  589. [4, 3, 4, 3, 2, 3, 1, 0, 2],
  590. [4, 3, 4, 3, 2, 3, 2, 2, 0],
  591. ],
  592. [
  593. [-640.0000, 736.0000, 96.0000, 0.0000, -224.0000, 96.0000, 0.0000, 64.0000, 64.0000],
  594. [ 736.0000, -1472.0000, 0.0000, 0.0000, 736.0000, 0.0000, 0.0000, 0.0000, 0.0000],
  595. [ 96.0000, 0.0000, -640.0000, 736.0000, -224.0000, 96.0000, 0.0000, 64.0000, 64.0000],
  596. [ -0.0000, 0.0000, 736.0000, -1472.0000, 736.0000, 0.0000, 0.0000, 0.0000, 0.0000],
  597. [-224.0000, 736.0000, -224.0000, 736.0000, -2544.0000, 512.0000, 736.0000, -272.0000, 96.0000],
  598. [ 96.0000, 0.0000, 96.0000, 0.0000, 512.0000, -640.0000, 0.0000, 64.0000, 64.0000],
  599. [ -0.0000, 0.0000, 0.0000, 0.0000, 736.0000, 0.0000, -1472.0000, 736.0000, 0.0000],
  600. [ 64.0000, 0.0000, 64.0000, 0.0000, -272.0000, 64.0000, 736.0000, -816.0000, 288.0000],
  601. [ 64.0000, 0.0000, 64.0000, 0.0000, 96.0000, 64.0000, 0.0000, 288.0000, -448.0000],
  602. ],
  603. ],
  604. [
  605. [
  606. [-1, 2, 3],
  607. [1, 5, 6],
  608. [0, 4, 3],
  609. ],
  610. [
  611. [-9, -3, 4],
  612. [6, -3, 4],
  613. [-3, 9, -7],
  614. ],
  615. ],
  616. [
  617. [
  618. [0, 1, 4],
  619. [1, 0, 3],
  620. [4, 3, 0],
  621. ],
  622. [
  623. [-9, 12, 3],
  624. [12, -16, 4],
  625. [3, 4, -1],
  626. ],
  627. ],
  628. ];
  629. }
  630. /**
  631. * @test cofactorMatrix exception - not square
  632. */
  633. public function testCofactorMatrixExceptionNotSquare()
  634. {
  635. // Given
  636. $A = MatrixFactory::create([
  637. [1, 2, 3, 4],
  638. [2, 3, 4, 4],
  639. [3, 4, 5, 4],
  640. ]);
  641. // Then
  642. $this->expectException(Exception\MatrixException::class);
  643. // When
  644. $A->cofactorMatrix();
  645. }
  646. /**
  647. * @test cofactorMatrix exception - too small
  648. */
  649. public function testCofactorMatrixExceptionTooSmall()
  650. {
  651. // Given
  652. $A = MatrixFactory::create([
  653. [1],
  654. ]);
  655. // Then
  656. $this->expectException(Exception\MatrixException::class);
  657. // When
  658. $A->cofactorMatrix();
  659. }
  660. /**
  661. * @test meanDeviation
  662. * @dataProvider dataProviderForMeanDeviation
  663. */
  664. public function testMeanDeviation(array $A, array $B)
  665. {
  666. // Given
  667. $A = MatrixFactory::create($A);
  668. $B = MatrixFactory::create($B);
  669. // When
  670. $meanDeviation = $A->meanDeviation();
  671. // Then
  672. $this->assertEquals($B, $meanDeviation);
  673. }
  674. public function dataProviderForMeanDeviation(): array
  675. {
  676. return [
  677. // Test data from: http://www.maths.manchester.ac.uk/~mkt/MT3732%20(MVA)/Intro.pdf
  678. [
  679. [
  680. [4, -1, 3],
  681. [1, 3, 5],
  682. ],
  683. [
  684. [2, -3, 1],
  685. [-2, 0, 2],
  686. ],
  687. ],
  688. // Test data from Linear Algebra and Its Applications (Lay)
  689. [
  690. [
  691. [1, 4, 7, 8],
  692. [2, 2, 8, 4],
  693. [1, 13, 1, 5],
  694. ],
  695. [
  696. [-4, -1, 2, 3],
  697. [-2, -2, 4, 0],
  698. [-4, 8, -4, 0],
  699. ],
  700. ],
  701. [
  702. [
  703. [19, 22, 6, 3, 2, 20],
  704. [12, 6, 9, 15, 13, 5],
  705. ],
  706. [
  707. [7, 10, -6, -9, -10, 8],
  708. [2, -4, -1, 5, 3, -5],
  709. ],
  710. ],
  711. ];
  712. }
  713. /**
  714. * @test meanDeviation column as variables
  715. * @dataProvider dataProviderForMeanDeviationColumnsAsVariables
  716. */
  717. public function testMeanDeviationColumnsAsVariables(array $A, array $B)
  718. {
  719. // Given
  720. $A = MatrixFactory::create($A);
  721. $B = MatrixFactory::create($B);
  722. // When
  723. $meanDeviation = $A->meanDeviation('columns');
  724. // Then
  725. $this->assertEqualsWithDelta($B, $meanDeviation, 0.00001);
  726. }
  727. public function dataProviderForMeanDeviationColumnsAsVariables(): array
  728. {
  729. return [
  730. [
  731. [
  732. [3, 5, 1],
  733. [9, 1, 4],
  734. ],
  735. [
  736. [-3, 2, -1.5],
  737. [3, -2, 1.5],
  738. ],
  739. ],
  740. [
  741. [
  742. [4, -1, 3],
  743. [1, 3, 5],
  744. ],
  745. [
  746. [1.5, -2, -1],
  747. [-1.5, 2, 1],
  748. ],
  749. ],
  750. [
  751. [
  752. [1, 4, 7, 8],
  753. [2, 2, 8, 4],
  754. [1, 13, 1, 5],
  755. ],
  756. [
  757. [-1 / 3, -7 / 3, 5 / 3, 7 / 3],
  758. [ 2 / 3, -13 / 3, 8 / 3, -5 / 3],
  759. [-1 / 3, 20 / 3, -13 / 3, -2 / 3],
  760. ],
  761. ],
  762. [
  763. [
  764. [90, 60, 90],
  765. [90, 90, 30],
  766. [60, 60, 60],
  767. [60, 60, 90],
  768. [30, 30, 30],
  769. ],
  770. [
  771. [24, 0, 30],
  772. [24, 30, -30],
  773. [-6, 0, 0],
  774. [-6, 0, 30],
  775. [-36, -30, -30],
  776. ],
  777. ],
  778. ];
  779. }
  780. /**
  781. * @test meanDeviation exception is direction is not rows or columns
  782. * @throws \Exception
  783. */
  784. public function testMeanDeviationDirectionException()
  785. {
  786. // Given
  787. $A = MatrixFactory::create([
  788. [1, 2, 3],
  789. [2, 3, 4],
  790. ]);
  791. // Then
  792. $this->expectException(Exception\BadParameterException::class);
  793. // When
  794. $A->meanDeviation('diagonal_direction');
  795. }
  796. /**
  797. * @test covarianceMatrix
  798. * @dataProvider dataProviderForCovarianceMatrix
  799. */
  800. public function testCovarianceMatrix(array $A, array $S)
  801. {
  802. // Given
  803. $A = MatrixFactory::create($A);
  804. // When
  805. $covarianceMatrix = $A->covarianceMatrix();
  806. // Then
  807. $this->assertEqualsWithDelta($S, $covarianceMatrix->getMatrix(), 0.0001);
  808. }
  809. public function dataProviderForCovarianceMatrix(): array
  810. {
  811. return [
  812. // Test data from Linear Algebra and Its Applications (Lay)
  813. [
  814. [
  815. [1, 4, 7, 8],
  816. [2, 2, 8, 4],
  817. [1, 13, 1, 5],
  818. ],
  819. [
  820. [10, 6, 0],
  821. [6, 8, -8],
  822. [0, -8, 32],
  823. ],
  824. ],
  825. [
  826. [
  827. [19, 22, 6, 3, 2, 20],
  828. [12, 6, 9, 15, 13, 5],
  829. ],
  830. [
  831. [86, -27],
  832. [-27, 16],
  833. ],
  834. ],
  835. // Test data from: http://www.itl.nist.gov/div898/handbook/pmc/section5/pmc541.htm
  836. [
  837. [
  838. [4, 4.2, 3.9, 4.3, 4.1],
  839. [2, 2.1, 2, 2.1, 2.2],
  840. [.6, .59, .58, .62, .63]
  841. ],
  842. [
  843. [0.025, 0.0075, 0.00175],
  844. [0.0075, 0.007, 0.00135],
  845. [0.00175, 0.00135, 0.00043],
  846. ],
  847. ],
  848. [
  849. [
  850. [2.5, 0.5, 2.2, 1.9, 3.1, 2.3, 2, 1, 1.5, 1.1],
  851. [2.4, 0.7, 2.9, 2.2, 3.0, 2.7, 1.6, 1.1, 1.6, 0.9],
  852. ],
  853. [
  854. [0.616555556, 0.615444444],
  855. [0.615444444, 0.716555556],
  856. ],
  857. ],
  858. // Test data from: https://www.mathworks.com/help/matlab/ref/cov.html
  859. [
  860. [
  861. [5, 1, 4],
  862. [0, -5, 9],
  863. [3, 7, 8],
  864. [7, 3, 10],
  865. ],
  866. [
  867. [4.3333, 8.8333, -3.0000, 5.6667],
  868. [8.8333, 50.3333, 6.5000, 24.1667],
  869. [-3.0000, 6.5000, 7.0000, 1.0000],
  870. [5.6667, 24.1667, 1.0000, 12.3333],
  871. ],
  872. ],
  873. // Test data from: http://stats.seandolinar.com/making-a-covariance-matrix-in-r/
  874. [
  875. [
  876. [1, 2, 3, 4, 5, 6],
  877. [2, 3, 5, 6, 1, 9],
  878. [3, 5, 5, 5, 10, 8],
  879. [10, 20, 30,40, 50, 55],
  880. [7, 8, 9, 4, 6, 10],
  881. ],
  882. [
  883. [ 3.5, 3.000000, 4.0, 32.500000, 0.400000],
  884. [ 3.0, 8.666667, 0.4, 25.333333, 2.466667],
  885. [ 4.0, 0.400000, 6.4, 38.000000, 0.400000],
  886. [32.5, 25.333333, 38.0, 304.166667, 1.333333],
  887. [ 0.4, 2.466667, 0.4, 1.333333, 4.666667],
  888. ],
  889. ],
  890. ];
  891. }
  892. /**
  893. * @test covarianceMatrix columns as variables
  894. * @dataProvider dataProviderForCovarianceMatrixColumnsAsVariables
  895. */
  896. public function testCovarianceMatrixColumnsAsVariables(array $A, array $S)
  897. {
  898. // Given
  899. $A = MatrixFactory::create($A);
  900. $direction = NumericMatrix::COLUMNS;
  901. // When
  902. $covarianceMatrix = $A->covarianceMatrix($direction);
  903. // Then
  904. $this->assertEqualsWithDelta($S, $covarianceMatrix->getMatrix(), 0.0001);
  905. }
  906. /**
  907. * Data generated with R cov(A)
  908. * @return array
  909. */
  910. public function dataProviderForCovarianceMatrixColumnsAsVariables(): array
  911. {
  912. return [
  913. [
  914. [
  915. [90, 60, 90],
  916. [90, 90, 30],
  917. [60, 60, 60],
  918. [60, 60, 90],
  919. [30, 30, 30],
  920. ],
  921. [
  922. [630, 450, 225],
  923. [450, 450, 0],
  924. [225, 0, 900],
  925. ],
  926. ],
  927. [
  928. [
  929. [2, -2],
  930. [8, -1],
  931. [6, 0],
  932. [4, 1],
  933. [10, 2],
  934. ],
  935. [
  936. [10, 3],
  937. [3, 2.5],
  938. ],
  939. ],
  940. [
  941. [
  942. [1, 4, 7, 8],
  943. [2, 2, 8, 4],
  944. [1, 13, 1, 5],
  945. ],
  946. [
  947. [0.3333333, -2.166667, 1.333333, -0.8333333],
  948. [-2.1666667, 34.333333, -22.166667, -1.3333333],
  949. [1.3333333, -22.166667, 14.333333, 1.1666667],
  950. [-0.8333333, -1.333333, 1.166667, 4.3333333],
  951. ],
  952. ],
  953. [
  954. [
  955. [19, 22, 6, 3, 2, 20],
  956. [12, 6, 9, 15, 13, 5],
  957. ],
  958. [
  959. [ 24.5, 56, -10.5, -42, -38.5, 52.5],
  960. [ 56.0, 128, -24.0, -96, -88.0, 120.0],
  961. [-10.5, -24, 4.5, 18, 16.5, -22.5],
  962. [-42.0, -96, 18.0, 72, 66.0, -90.0],
  963. [-38.5, -88, 16.5, 66, 60.5, -82.5],
  964. [ 52.5, 120, -22.5, -90, -82.5, 112.5],
  965. ],
  966. ],
  967. ];
  968. }
  969. /**
  970. * @test covarianceMatrix exception is direction is not rows or columns
  971. * @throws \Exception
  972. */
  973. public function testCovarianceMatrixDirectionException()
  974. {
  975. // Given
  976. $A = MatrixFactory::create([
  977. [1, 2, 3],
  978. [2, 3, 4],
  979. ]);
  980. // Then
  981. $this->expectException(Exception\BadParameterException::class);
  982. // When
  983. $covarianceMatrix = $A->covarianceMatrix('invalid_direction');
  984. }
  985. /**
  986. * @test adjugate returns the expected SquareMatrix
  987. * @dataProvider dataProviderForAdjugate
  988. * @param array $A
  989. * @param array $expected
  990. */
  991. public function testAdjugate(array $A, array $expected)
  992. {
  993. // Given
  994. $A = MatrixFactory::create($A);
  995. $expected = MatrixFactory::create($expected);
  996. // When
  997. $adj⟮A⟯ = $A->adjugate();
  998. // Then
  999. $this->assertEqualsWithDelta($expected, $adj⟮A⟯, 0.00001);
  1000. $this->assertEqualsWithDelta($expected->getMatrix(), $adj⟮A⟯->getMatrix(), 0.00001);
  1001. }
  1002. public function dataProviderForAdjugate(): array
  1003. {
  1004. return [
  1005. [
  1006. [
  1007. [0],
  1008. ],
  1009. [
  1010. [1],
  1011. ],
  1012. ],
  1013. [
  1014. [
  1015. [1],
  1016. ],
  1017. [
  1018. [1],
  1019. ],
  1020. ],
  1021. [
  1022. [
  1023. [5],
  1024. ],
  1025. [
  1026. [1],
  1027. ],
  1028. ],
  1029. // Data calculated using online calculator: http://www.dcode.fr/adjoint-matrix
  1030. [
  1031. [
  1032. [1, 2],
  1033. [3, 4],
  1034. ],
  1035. [
  1036. [4, -2],
  1037. [-3, 1],
  1038. ],
  1039. ],
  1040. [
  1041. [
  1042. [4, -2],
  1043. [-3, 1],
  1044. ],
  1045. [
  1046. [1, 2],
  1047. [3, 4],
  1048. ],
  1049. ],
  1050. [
  1051. [
  1052. [1, 1, 1],
  1053. [1, 1, 1],
  1054. [1, 1, 1],
  1055. ],
  1056. [
  1057. [0, 0, 0],
  1058. [0, 0, 0],
  1059. [0, 0, 0],
  1060. ],
  1061. ],
  1062. [
  1063. [
  1064. [0, 0, 0],
  1065. [0, 0, 0],
  1066. [0, 0, 0],
  1067. ],
  1068. [
  1069. [0, 0, 0],
  1070. [0, 0, 0],
  1071. [0, 0, 0],
  1072. ],
  1073. ],
  1074. [
  1075. [
  1076. [1, 2, 3],
  1077. [4, 5, 6],
  1078. [7, 8, 9],
  1079. ],
  1080. [
  1081. [-3, 6, -3],
  1082. [6, -12, 6],
  1083. [-3, 6, -3],
  1084. ],
  1085. ],
  1086. [
  1087. [
  1088. [1, 2, 3],
  1089. [4, 5, 6],
  1090. [7, 8, 9],
  1091. ],
  1092. [
  1093. [-3, 6, -3],
  1094. [6, -12, 6],
  1095. [-3, 6, -3],
  1096. ],
  1097. ],
  1098. [
  1099. [
  1100. [1, 3, 4],
  1101. [3, 4, 4],
  1102. [4, 3, 2],
  1103. ],
  1104. [
  1105. [-4, 6, -4],
  1106. [10, -14, 8],
  1107. [-7, 9, -5],
  1108. ],
  1109. ],
  1110. [
  1111. [
  1112. [4, 8, 5, 4],
  1113. [3, 5, 7, 9],
  1114. [0, 8, 12, 5],
  1115. [7, 9, 0, 3],
  1116. ],
  1117. [
  1118. [-645, 39, 246, 333],
  1119. [403, -17, -158, -223],
  1120. [-392, 28, 136, 212],
  1121. [296, -40, -100, -152],
  1122. ],
  1123. ],
  1124. [
  1125. [
  1126. [2, -1, 4, 3, 2, 3, 3, 4, 4],
  1127. [-1, 2, 3, 2, 1, 2, 2, 3, 3],
  1128. [4, 3, 2, 1, 2, 3, 3, 4, 4],
  1129. [2, 1, 2, 1, 2, 1, 1, 2, 2],
  1130. [3, 2, 1, 2, 1, 2, 2, 3, 3],
  1131. [3, 2, 3, 2, 1, 2, 2, 3, 3],
  1132. [3, 2, 3, 2, 1, 2, 2, 1, 2],
  1133. [4, 3, 4, 3, 2, 3, 1, 2, 2],
  1134. [4, 3, 4, 3, 2, 3, 2, 2, 2],
  1135. ],
  1136. [
  1137. [0, 128, 0, 0, 0, -128, 0, 0, -0],
  1138. [128, -80, 0, -32, -32, -16, 0, 32, -64],
  1139. [0, 0, 0, 0, 256, -256, 0, 0, -0],
  1140. [0, -32, 256, -64, -320, 96, 0, 64, -128],
  1141. [0, -32, 0, -320, -64, 352, 0, 64, -128],
  1142. [-128, -16, -256, 352, 96, 304, -0, -352, 192],
  1143. [-0, 0, -0, 2.8421709430404E-14, 0, -0, 0, 512, -512],
  1144. [-0, 32, -0, 64, 64, -352, 512, 192, -384],
  1145. [0, -64, 0, -128, -128, 192, -512, -384, 768],
  1146. ]
  1147. ],
  1148. [
  1149. [
  1150. [0, 1, 4, 3, 2, 3, 3, 4, 4],
  1151. [1, 0, 3, 2, 1, 2, 2, 3, 3],
  1152. [4, 3, 0, 1, 2, 3, 3, 4, 4],
  1153. [3, 2, 1, 0, 1, 2, 2, 3, 3],
  1154. [2, 1, 2, 1, 0, 1, 1, 2, 2],
  1155. [3, 2, 3, 2, 1, 0, 2, 3, 3],
  1156. [3, 2, 3, 2, 1, 2, 0, 1, 2],
  1157. [4, 3, 4, 3, 2, 3, 1, 0, 2],
  1158. [4, 3, 4, 3, 2, 3, 2, 2, 0],
  1159. ],
  1160. [
  1161. [-640, 736, 96, 0, -224, 96, 0, 64, 64],
  1162. [736, -1472, 0, 0, 736, 0, 0, 0, 0],
  1163. [96, 0, -640, 736, -224, 96, 0, 64, 64],
  1164. [0, 0, 736, -1472, 736, 0, 0, 0, 0, ],
  1165. [-224, 736, -224, 736, -2544, 512, 736, -272, 96],
  1166. [96, 0, 96, 0, 512, -640, 0, 64, 64],
  1167. [0, 0, 0, 0, 736, 0, -1472, 736, 0],
  1168. [64, 0, 64, 0, -272, 64, 736, -816, 288],
  1169. [64, 0, 64, 0, 96, 64, 0, 288, -448],
  1170. ]
  1171. ],
  1172. ];
  1173. }
  1174. /**
  1175. * @test adjugate throws an Exception\MatrixException if the matrix is not square
  1176. */
  1177. public function testAdjugateSquareMatrixException()
  1178. {
  1179. // Given
  1180. $A = [
  1181. [1, 2, 3],
  1182. [2, 3, 4],
  1183. ];
  1184. $A = MatrixFactory::create($A);
  1185. // Then
  1186. $this->expectException(Exception\MatrixException::class);
  1187. // When
  1188. $adj⟮A⟯ = $A->adjugate();
  1189. }
  1190. /**
  1191. * @test rank returns the expected value
  1192. * @dataProvider dataProviderForRank
  1193. */
  1194. public function testRank(array $A, $expected)
  1195. {
  1196. // Given
  1197. $A = MatrixFactory::create($A);
  1198. // When
  1199. $rank = $A->rank();
  1200. $this->assertEquals($expected, $rank);
  1201. }
  1202. public function dataProviderForRank(): array
  1203. {
  1204. return [
  1205. [
  1206. [
  1207. [0]
  1208. ], 0
  1209. ],
  1210. [
  1211. [
  1212. [1]
  1213. ], 1
  1214. ],
  1215. [
  1216. [
  1217. [2]
  1218. ], 1
  1219. ],
  1220. [
  1221. [
  1222. [-2]
  1223. ], 1
  1224. ],
  1225. [
  1226. [
  1227. [1, 2, 3],
  1228. [2, 3, 4],
  1229. [3, 4, 5],
  1230. ], 2
  1231. ],
  1232. [
  1233. [
  1234. [1, 3, -1],
  1235. [0, 1, 7],
  1236. ], 2
  1237. ],
  1238. [
  1239. [
  1240. [1, 2, 1],
  1241. [-2, -3, 1],
  1242. [3, 5, 0],
  1243. ], 2
  1244. ],
  1245. [
  1246. [
  1247. [0, 3, -6, 6, 4, -5],
  1248. [3, -7, 8, -5, 8, 9],
  1249. [3, -9, 12, -9, 6, 15],
  1250. ], 3
  1251. ],
  1252. [
  1253. [
  1254. [0, 2, 8, -7],
  1255. [2, -2, 4, 0],
  1256. [-3, 4, -2, -5],
  1257. ], 3
  1258. ],
  1259. [
  1260. [
  1261. [1, -2, 3, 9],
  1262. [-1, 3, 0, -4],
  1263. [2, -5, 5, 17],
  1264. ], 3
  1265. ],
  1266. [
  1267. [
  1268. [1, 0, -2, 1, 0],
  1269. [0, -1, -3, 1, 3],
  1270. [-2, -1, 1, -1, 3],
  1271. [0, 3, 9, 0, -12],
  1272. ], 3
  1273. ],
  1274. [
  1275. [
  1276. [1, 1, 4, 1, 2],
  1277. [0, 1, 2, 1, 1],
  1278. [0, 0, 0, 1, 2],
  1279. [1, -1, 0, 0, 2],
  1280. [2, 1, 6, 0, 1],
  1281. ], 3
  1282. ],
  1283. [
  1284. [
  1285. [1, 2, 0, -1, 1, -10],
  1286. [1, 3, 1, 1, -1, -9],
  1287. [2, 5, 1, 0, 0, -19],
  1288. [3, 6, 0, 0, -6, -27],
  1289. [1, 5, 3, 5, -5, -7],
  1290. ], 3
  1291. ],
  1292. [
  1293. [
  1294. [-4, 3, 1, 5, -8],
  1295. [6, 0, 9, 2, 6],
  1296. [-1, 4, 4, 0, 2],
  1297. [8, -1, 3, 4, 0],
  1298. [5, 9, -7, -7, 1],
  1299. ], 5
  1300. ],
  1301. [
  1302. [
  1303. [4, 7],
  1304. [2, 6],
  1305. ], 2
  1306. ],
  1307. [
  1308. [
  1309. [4, 3],
  1310. [3, 2],
  1311. ], 2
  1312. ],
  1313. [
  1314. [
  1315. [1, 2],
  1316. [3, 4],
  1317. ], 2
  1318. ],
  1319. [
  1320. [
  1321. [1, 2, 3],
  1322. [0, 4, 5],
  1323. [1, 0, 6],
  1324. ], 3
  1325. ],
  1326. [
  1327. [
  1328. [7, 2, 1],
  1329. [0, 3, -1],
  1330. [-3, 4, -2],
  1331. ], 3
  1332. ],
  1333. [
  1334. [
  1335. [3, 6, 6, 8],
  1336. [4, 5, 3, 2],
  1337. [2, 2, 2, 3],
  1338. [6, 8, 4, 2],
  1339. ], 4
  1340. ],
  1341. [
  1342. [
  1343. [0, 0],
  1344. [0, 1],
  1345. ], 1
  1346. ],
  1347. [
  1348. [
  1349. [1, 1, 1, 1, 1],
  1350. [0, 1, 1, 1, 1],
  1351. [0, 0, 0, 0, 1],
  1352. ], 3
  1353. ],
  1354. [
  1355. [
  1356. [0, 0],
  1357. [1, 1],
  1358. [-1, 0],
  1359. [0, -1],
  1360. [0, 0],
  1361. [0, 0],
  1362. [0, 0],
  1363. [0, 0],
  1364. [1, 1],
  1365. ], 2
  1366. ],
  1367. [
  1368. [
  1369. [1, 2, 3, 4, 3, 1],
  1370. [2, 4, 6, 2, 6, 2],
  1371. [3, 6, 18, 9, 9, -6],
  1372. [4, 8, 12, 10, 12, 4],
  1373. [5, 10, 24, 11, 15, -4],
  1374. ], 3
  1375. ],
  1376. [
  1377. [
  1378. [1, 2, 3, 4, 3, 1],
  1379. [2, 4, 6, 2, 6, 2],
  1380. [3, 6, 18, 9, 9, -6],
  1381. [4, 8, 12, 10, 12, 4],
  1382. [5, 10, 24, 11, 15, -4]
  1383. ], 3
  1384. ],
  1385. [
  1386. [
  1387. [0, 1],
  1388. [1, 2],
  1389. [0, 5],
  1390. ], 2
  1391. ],
  1392. [
  1393. [
  1394. [1, 0, 1, 0, 1, 0],
  1395. [1, 0, 1, 0, 0, 1],
  1396. [1, 0, 0, 1, 1, 0],
  1397. [1, 0, 0, 1, 0, 1],
  1398. [0, 1, 0, 1, 1, 0],
  1399. [0, 1, 0, 1, 0, 1],
  1400. [0, 1, 1, 0, 1, 0],
  1401. [0, 1, 1, 0, 0, 1],
  1402. ], 4
  1403. ],
  1404. ];
  1405. }
  1406. }