You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

338 lines
9.6 KiB

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