PageRenderTime 39ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/mlbackend/php/phpml/src/Phpml/Math/Matrix.php

https://gitlab.com/unofficial-mirrors/moodle
PHP | 374 lines | 197 code | 49 blank | 128 comment | 31 complexity | 9078eefffac6bdf005a310ab4050aaeb MD5 | raw file
  1. <?php
  2. declare(strict_types=1);
  3. namespace Phpml\Math;
  4. use Phpml\Math\LinearAlgebra\LUDecomposition;
  5. use Phpml\Exception\InvalidArgumentException;
  6. use Phpml\Exception\MatrixException;
  7. class Matrix
  8. {
  9. /**
  10. * @var array
  11. */
  12. private $matrix;
  13. /**
  14. * @var int
  15. */
  16. private $rows;
  17. /**
  18. * @var int
  19. */
  20. private $columns;
  21. /**
  22. * @var float
  23. */
  24. private $determinant;
  25. /**
  26. * @param array $matrix
  27. * @param bool $validate
  28. *
  29. * @throws InvalidArgumentException
  30. */
  31. public function __construct(array $matrix, bool $validate = true)
  32. {
  33. // When a row vector is given
  34. if (!is_array($matrix[0])) {
  35. $this->rows = 1;
  36. $this->columns = count($matrix);
  37. $matrix = [$matrix];
  38. } else {
  39. $this->rows = count($matrix);
  40. $this->columns = count($matrix[0]);
  41. }
  42. if ($validate) {
  43. for ($i = 0; $i < $this->rows; ++$i) {
  44. if (count($matrix[$i]) !== $this->columns) {
  45. throw InvalidArgumentException::matrixDimensionsDidNotMatch();
  46. }
  47. }
  48. }
  49. $this->matrix = $matrix;
  50. }
  51. /**
  52. * @param array $array
  53. *
  54. * @return Matrix
  55. */
  56. public static function fromFlatArray(array $array)
  57. {
  58. $matrix = [];
  59. foreach ($array as $value) {
  60. $matrix[] = [$value];
  61. }
  62. return new self($matrix);
  63. }
  64. /**
  65. * @return array
  66. */
  67. public function toArray()
  68. {
  69. return $this->matrix;
  70. }
  71. /**
  72. * @return float
  73. */
  74. public function toScalar()
  75. {
  76. return $this->matrix[0][0];
  77. }
  78. /**
  79. * @return int
  80. */
  81. public function getRows()
  82. {
  83. return $this->rows;
  84. }
  85. /**
  86. * @return int
  87. */
  88. public function getColumns()
  89. {
  90. return $this->columns;
  91. }
  92. /**
  93. * @param $column
  94. *
  95. * @return array
  96. *
  97. * @throws MatrixException
  98. */
  99. public function getColumnValues($column)
  100. {
  101. if ($column >= $this->columns) {
  102. throw MatrixException::columnOutOfRange();
  103. }
  104. return array_column($this->matrix, $column);
  105. }
  106. /**
  107. * @return float|int
  108. *
  109. * @throws MatrixException
  110. */
  111. public function getDeterminant()
  112. {
  113. if ($this->determinant) {
  114. return $this->determinant;
  115. }
  116. if (!$this->isSquare()) {
  117. throw MatrixException::notSquareMatrix();
  118. }
  119. $lu = new LUDecomposition($this);
  120. return $this->determinant = $lu->det();
  121. }
  122. /**
  123. * @return bool
  124. */
  125. public function isSquare()
  126. {
  127. return $this->columns === $this->rows;
  128. }
  129. /**
  130. * @return Matrix
  131. */
  132. public function transpose()
  133. {
  134. if ($this->rows == 1) {
  135. $matrix = array_map(function ($el) {
  136. return [$el];
  137. }, $this->matrix[0]);
  138. } else {
  139. $matrix = array_map(null, ...$this->matrix);
  140. }
  141. return new self($matrix, false);
  142. }
  143. /**
  144. * @param Matrix $matrix
  145. *
  146. * @return Matrix
  147. *
  148. * @throws InvalidArgumentException
  149. */
  150. public function multiply(Matrix $matrix)
  151. {
  152. if ($this->columns != $matrix->getRows()) {
  153. throw InvalidArgumentException::inconsistentMatrixSupplied();
  154. }
  155. $product = [];
  156. $multiplier = $matrix->toArray();
  157. for ($i = 0; $i < $this->rows; ++$i) {
  158. $columns = $matrix->getColumns();
  159. for ($j = 0; $j < $columns; ++$j) {
  160. $product[$i][$j] = 0;
  161. for ($k = 0; $k < $this->columns; ++$k) {
  162. $product[$i][$j] += $this->matrix[$i][$k] * $multiplier[$k][$j];
  163. }
  164. }
  165. }
  166. return new self($product, false);
  167. }
  168. /**
  169. * @param $value
  170. *
  171. * @return Matrix
  172. */
  173. public function divideByScalar($value)
  174. {
  175. $newMatrix = [];
  176. for ($i = 0; $i < $this->rows; ++$i) {
  177. for ($j = 0; $j < $this->columns; ++$j) {
  178. $newMatrix[$i][$j] = $this->matrix[$i][$j] / $value;
  179. }
  180. }
  181. return new self($newMatrix, false);
  182. }
  183. /**
  184. * @param $value
  185. *
  186. * @return Matrix
  187. */
  188. public function multiplyByScalar($value)
  189. {
  190. $newMatrix = [];
  191. for ($i = 0; $i < $this->rows; ++$i) {
  192. for ($j = 0; $j < $this->columns; ++$j) {
  193. $newMatrix[$i][$j] = $this->matrix[$i][$j] * $value;
  194. }
  195. }
  196. return new self($newMatrix, false);
  197. }
  198. /**
  199. * Element-wise addition of the matrix with another one
  200. *
  201. * @param Matrix $other
  202. *
  203. * @return Matrix
  204. */
  205. public function add(Matrix $other)
  206. {
  207. return $this->_add($other);
  208. }
  209. /**
  210. * Element-wise subtracting of another matrix from this one
  211. *
  212. * @param Matrix $other
  213. *
  214. * @return Matrix
  215. */
  216. public function subtract(Matrix $other)
  217. {
  218. return $this->_add($other, -1);
  219. }
  220. /**
  221. * Element-wise addition or substraction depending on the given sign parameter
  222. *
  223. * @param Matrix $other
  224. * @param int $sign
  225. *
  226. * @return Matrix
  227. */
  228. protected function _add(Matrix $other, $sign = 1)
  229. {
  230. $a1 = $this->toArray();
  231. $a2 = $other->toArray();
  232. $newMatrix = [];
  233. for ($i = 0; $i < $this->rows; ++$i) {
  234. for ($k = 0; $k < $this->columns; ++$k) {
  235. $newMatrix[$i][$k] = $a1[$i][$k] + $sign * $a2[$i][$k];
  236. }
  237. }
  238. return new self($newMatrix, false);
  239. }
  240. /**
  241. * @return Matrix
  242. *
  243. * @throws MatrixException
  244. */
  245. public function inverse()
  246. {
  247. if (!$this->isSquare()) {
  248. throw MatrixException::notSquareMatrix();
  249. }
  250. $LU = new LUDecomposition($this);
  251. $identity = $this->getIdentity();
  252. $inverse = $LU->solve($identity);
  253. return new self($inverse, false);
  254. }
  255. /**
  256. * Returns diagonal identity matrix of the same size of this matrix
  257. *
  258. * @return Matrix
  259. */
  260. protected function getIdentity()
  261. {
  262. $array = array_fill(0, $this->rows, array_fill(0, $this->columns, 0));
  263. for ($i = 0; $i < $this->rows; ++$i) {
  264. $array[$i][$i] = 1;
  265. }
  266. return new self($array, false);
  267. }
  268. /**
  269. * @param int $row
  270. * @param int $column
  271. *
  272. * @return Matrix
  273. */
  274. public function crossOut(int $row, int $column)
  275. {
  276. $newMatrix = [];
  277. $r = 0;
  278. for ($i = 0; $i < $this->rows; ++$i) {
  279. $c = 0;
  280. if ($row != $i) {
  281. for ($j = 0; $j < $this->columns; ++$j) {
  282. if ($column != $j) {
  283. $newMatrix[$r][$c] = $this->matrix[$i][$j];
  284. ++$c;
  285. }
  286. }
  287. ++$r;
  288. }
  289. }
  290. return new self($newMatrix, false);
  291. }
  292. /**
  293. * @return bool
  294. */
  295. public function isSingular() : bool
  296. {
  297. return 0 == $this->getDeterminant();
  298. }
  299. /**
  300. * Returns the transpose of given array
  301. *
  302. * @param array $array
  303. *
  304. * @return array
  305. */
  306. public static function transposeArray(array $array)
  307. {
  308. return (new self($array, false))->transpose()->toArray();
  309. }
  310. /**
  311. * Returns the dot product of two arrays<br>
  312. * Matrix::dot(x, y) ==> x.y'
  313. *
  314. * @param array $array1
  315. * @param array $array2
  316. *
  317. * @return array
  318. */
  319. public static function dot(array $array1, array $array2)
  320. {
  321. $m1 = new self($array1, false);
  322. $m2 = new self($array2, false);
  323. return $m1->multiply($m2->transpose())->toArray()[0];
  324. }
  325. }