Functions.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. <?php
  2. namespace Matrix;
  3. class Functions
  4. {
  5. /**
  6. * Validates an array of matrix, converting an array to a matrix if required.
  7. *
  8. * @param Matrix|array $matrix Matrix or an array to treat as a matrix.
  9. * @return Matrix The new matrix
  10. * @throws Exception If argument isn't a valid matrix or array.
  11. */
  12. private static function validateMatrix($matrix)
  13. {
  14. if (is_array($matrix)) {
  15. $matrix = new Matrix($matrix);
  16. }
  17. if (!$matrix instanceof Matrix) {
  18. throw new Exception('Must be Matrix or array');
  19. }
  20. return $matrix;
  21. }
  22. /**
  23. * Calculate the adjoint of the matrix
  24. *
  25. * @param Matrix $matrix The matrix whose adjoint we wish to calculate
  26. * @return Matrix
  27. *
  28. * @throws Exception
  29. */
  30. private static function getAdjoint(Matrix $matrix)
  31. {
  32. return self::transpose(
  33. self::getCofactors($matrix)
  34. );
  35. }
  36. /**
  37. * Return the adjoint of this matrix
  38. * The adjugate, classical adjoint, or adjunct of a square matrix is the transpose of its cofactor matrix.
  39. * The adjugate has sometimes been called the "adjoint", but today the "adjoint" of a matrix normally refers
  40. * to its corresponding adjoint operator, which is its conjugate transpose.
  41. *
  42. * @param Matrix|array $matrix The matrix whose adjoint we wish to calculate
  43. * @return Matrix
  44. * @throws Exception
  45. **/
  46. public static function adjoint($matrix)
  47. {
  48. $matrix = self::validateMatrix($matrix);
  49. if (!$matrix->isSquare()) {
  50. throw new Exception('Adjoint can only be calculated for a square matrix');
  51. }
  52. return self::getAdjoint($matrix);
  53. }
  54. /**
  55. * Calculate the cofactors of the matrix
  56. *
  57. * @param Matrix $matrix The matrix whose cofactors we wish to calculate
  58. * @return Matrix
  59. *
  60. * @throws Exception
  61. */
  62. private static function getCofactors(Matrix $matrix)
  63. {
  64. $cofactors = self::getMinors($matrix);
  65. $dimensions = $matrix->rows;
  66. $cof = 1;
  67. for ($i = 0; $i < $dimensions; ++$i) {
  68. $cofs = $cof;
  69. for ($j = 0; $j < $dimensions; ++$j) {
  70. $cofactors[$i][$j] *= $cofs;
  71. $cofs = -$cofs;
  72. }
  73. $cof = -$cof;
  74. }
  75. return new Matrix($cofactors);
  76. }
  77. /**
  78. * Return the cofactors of this matrix
  79. *
  80. * @param Matrix|array $matrix The matrix whose cofactors we wish to calculate
  81. * @return Matrix
  82. *
  83. * @throws Exception
  84. */
  85. public static function cofactors($matrix)
  86. {
  87. $matrix = self::validateMatrix($matrix);
  88. if (!$matrix->isSquare()) {
  89. throw new Exception('Cofactors can only be calculated for a square matrix');
  90. }
  91. return self::getCofactors($matrix);
  92. }
  93. /**
  94. * @param Matrix $matrix
  95. * @param int $row
  96. * @param int $column
  97. * @return float
  98. * @throws Exception
  99. */
  100. private static function getDeterminantSegment(Matrix $matrix, $row, $column)
  101. {
  102. $tmpMatrix = $matrix->toArray();
  103. unset($tmpMatrix[$row]);
  104. array_walk(
  105. $tmpMatrix,
  106. function (&$row) use ($column) {
  107. unset($row[$column]);
  108. }
  109. );
  110. return self::getDeterminant(new Matrix($tmpMatrix));
  111. }
  112. /**
  113. * Calculate the determinant of the matrix
  114. *
  115. * @param Matrix $matrix The matrix whose determinant we wish to calculate
  116. * @return float
  117. *
  118. * @throws Exception
  119. */
  120. private static function getDeterminant(Matrix $matrix)
  121. {
  122. $dimensions = $matrix->rows;
  123. $determinant = 0;
  124. switch ($dimensions) {
  125. case 1:
  126. $determinant = $matrix->getValue(1, 1);
  127. break;
  128. case 2:
  129. $determinant = $matrix->getValue(1, 1) * $matrix->getValue(2, 2) -
  130. $matrix->getValue(1, 2) * $matrix->getValue(2, 1);
  131. break;
  132. default:
  133. for ($i = 1; $i <= $dimensions; ++$i) {
  134. $det = $matrix->getValue(1, $i) * self::getDeterminantSegment($matrix, 0, $i - 1);
  135. if (($i % 2) == 0) {
  136. $determinant -= $det;
  137. } else {
  138. $determinant += $det;
  139. }
  140. }
  141. break;
  142. }
  143. return $determinant;
  144. }
  145. /**
  146. * Return the determinant of this matrix
  147. *
  148. * @param Matrix|array $matrix The matrix whose determinant we wish to calculate
  149. * @return float
  150. * @throws Exception
  151. **/
  152. public static function determinant($matrix)
  153. {
  154. $matrix = self::validateMatrix($matrix);
  155. if (!$matrix->isSquare()) {
  156. throw new Exception('Determinant can only be calculated for a square matrix');
  157. }
  158. return self::getDeterminant($matrix);
  159. }
  160. /**
  161. * Return the diagonal of this matrix
  162. *
  163. * @param Matrix|array $matrix The matrix whose diagonal we wish to calculate
  164. * @return Matrix
  165. * @throws Exception
  166. **/
  167. public static function diagonal($matrix)
  168. {
  169. $matrix = self::validateMatrix($matrix);
  170. if (!$matrix->isSquare()) {
  171. throw new Exception('Diagonal can only be extracted from a square matrix');
  172. }
  173. $dimensions = $matrix->rows;
  174. $grid = Builder::createFilledMatrix(0, $dimensions, $dimensions)
  175. ->toArray();
  176. for ($i = 0; $i < $dimensions; ++$i) {
  177. $grid[$i][$i] = $matrix->getValue($i + 1, $i + 1);
  178. }
  179. return new Matrix($grid);
  180. }
  181. /**
  182. * Return the antidiagonal of this matrix
  183. *
  184. * @param Matrix|array $matrix The matrix whose antidiagonal we wish to calculate
  185. * @return Matrix
  186. * @throws Exception
  187. **/
  188. public static function antidiagonal($matrix)
  189. {
  190. $matrix = self::validateMatrix($matrix);
  191. if (!$matrix->isSquare()) {
  192. throw new Exception('Anti-Diagonal can only be extracted from a square matrix');
  193. }
  194. $dimensions = $matrix->rows;
  195. $grid = Builder::createFilledMatrix(0, $dimensions, $dimensions)
  196. ->toArray();
  197. for ($i = 0; $i < $dimensions; ++$i) {
  198. $grid[$i][$dimensions - $i - 1] = $matrix->getValue($i + 1, $dimensions - $i);
  199. }
  200. return new Matrix($grid);
  201. }
  202. /**
  203. * Return the identity matrix
  204. * The identity matrix, or sometimes ambiguously called a unit matrix, of size n is the n × n square matrix
  205. * with ones on the main diagonal and zeros elsewhere
  206. *
  207. * @param Matrix|array $matrix The matrix whose identity we wish to calculate
  208. * @return Matrix
  209. * @throws Exception
  210. **/
  211. public static function identity($matrix)
  212. {
  213. $matrix = self::validateMatrix($matrix);
  214. if (!$matrix->isSquare()) {
  215. throw new Exception('Identity can only be created for a square matrix');
  216. }
  217. $dimensions = $matrix->rows;
  218. return Builder::createIdentityMatrix($dimensions);
  219. }
  220. /**
  221. * Return the inverse of this matrix
  222. *
  223. * @param Matrix|array $matrix The matrix whose inverse we wish to calculate
  224. * @return Matrix
  225. * @throws Exception
  226. **/
  227. public static function inverse($matrix, string $type = 'inverse')
  228. {
  229. $matrix = self::validateMatrix($matrix);
  230. if (!$matrix->isSquare()) {
  231. throw new Exception(ucfirst($type) . ' can only be calculated for a square matrix');
  232. }
  233. $determinant = self::getDeterminant($matrix);
  234. if ($determinant == 0.0) {
  235. throw new Div0Exception(ucfirst($type) . ' can only be calculated for a matrix with a non-zero determinant');
  236. }
  237. if ($matrix->rows == 1) {
  238. return new Matrix([[1 / $matrix->getValue(1, 1)]]);
  239. }
  240. return self::getAdjoint($matrix)
  241. ->multiply(1 / $determinant);
  242. }
  243. /**
  244. * Calculate the minors of the matrix
  245. *
  246. * @param Matrix $matrix The matrix whose minors we wish to calculate
  247. * @return array[]
  248. *
  249. * @throws Exception
  250. */
  251. protected static function getMinors(Matrix $matrix)
  252. {
  253. $minors = $matrix->toArray();
  254. $dimensions = $matrix->rows;
  255. if ($dimensions == 1) {
  256. return $minors;
  257. }
  258. for ($i = 0; $i < $dimensions; ++$i) {
  259. for ($j = 0; $j < $dimensions; ++$j) {
  260. $minors[$i][$j] = self::getDeterminantSegment($matrix, $i, $j);
  261. }
  262. }
  263. return $minors;
  264. }
  265. /**
  266. * Return the minors of the matrix
  267. * The minor of a matrix A is the determinant of some smaller square matrix, cut down from A by removing one or
  268. * more of its rows or columns.
  269. * Minors obtained by removing just one row and one column from square matrices (first minors) are required for
  270. * calculating matrix cofactors, which in turn are useful for computing both the determinant and inverse of
  271. * square matrices.
  272. *
  273. * @param Matrix|array $matrix The matrix whose minors we wish to calculate
  274. * @return Matrix
  275. * @throws Exception
  276. **/
  277. public static function minors($matrix)
  278. {
  279. $matrix = self::validateMatrix($matrix);
  280. if (!$matrix->isSquare()) {
  281. throw new Exception('Minors can only be calculated for a square matrix');
  282. }
  283. return new Matrix(self::getMinors($matrix));
  284. }
  285. /**
  286. * Return the trace of this matrix
  287. * The trace is defined as the sum of the elements on the main diagonal (the diagonal from the upper left to the lower right)
  288. * of the matrix
  289. *
  290. * @param Matrix|array $matrix The matrix whose trace we wish to calculate
  291. * @return float
  292. * @throws Exception
  293. **/
  294. public static function trace($matrix)
  295. {
  296. $matrix = self::validateMatrix($matrix);
  297. if (!$matrix->isSquare()) {
  298. throw new Exception('Trace can only be extracted from a square matrix');
  299. }
  300. $dimensions = $matrix->rows;
  301. $result = 0;
  302. for ($i = 1; $i <= $dimensions; ++$i) {
  303. $result += $matrix->getValue($i, $i);
  304. }
  305. return $result;
  306. }
  307. /**
  308. * Return the transpose of this matrix
  309. *
  310. * @param Matrix|\a $matrix The matrix whose transpose we wish to calculate
  311. * @return Matrix
  312. **/
  313. public static function transpose($matrix)
  314. {
  315. $matrix = self::validateMatrix($matrix);
  316. $array = array_values(array_merge([null], $matrix->toArray()));
  317. $grid = call_user_func_array(
  318. 'array_map',
  319. $array
  320. );
  321. return new Matrix($grid);
  322. }
  323. }