PageRenderTime 31ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/branches/v1.6.5/Classes/PHPExcel/Shared/JAMA/Matrix.php

#
PHP | 1332 lines | 790 code | 189 blank | 353 comment | 159 complexity | 260e7fd7b00f436c7c9ef6a4f0863496 MD5 | raw file
Possible License(s): AGPL-1.0, LGPL-2.0, LGPL-2.1, GPL-3.0, LGPL-3.0
  1. <?php
  2. /**
  3. * @package JAMA
  4. */
  5. define('RAND_MAX', mt_getrandmax());
  6. define('RAND_MIN', 0);
  7. require_once 'PHPExcel/Shared/JAMA/utils/Error.php';
  8. require_once 'PHPExcel/Shared/JAMA/utils/Maths.php';
  9. require_once 'PHPExcel/Shared/JAMA/CholeskyDecomposition.php';
  10. require_once 'PHPExcel/Shared/JAMA/LUDecomposition.php';
  11. require_once 'PHPExcel/Shared/JAMA/QRDecomposition.php';
  12. require_once 'PHPExcel/Shared/JAMA/EigenvalueDecomposition.php';
  13. require_once 'PHPExcel/Shared/JAMA/SingularValueDecomposition.php';
  14. /*
  15. * Matrix class
  16. * @author Paul Meagher
  17. * @author Michael Bommarito
  18. * @author Lukasz Karapuda
  19. * @author Bartek Matosiuk
  20. * @version 1.8
  21. * @license PHP v3.0
  22. * @see http://math.nist.gov/javanumerics/jama/
  23. */
  24. class Matrix {
  25. /**
  26. * Matrix storage
  27. * @var array
  28. * @access private
  29. */
  30. var $A = array();
  31. /**
  32. * Matrix row dimension
  33. * @var int
  34. * @access private
  35. */
  36. var $m;
  37. /**
  38. * Matrix column dimension
  39. * @var int
  40. * @access private
  41. */
  42. var $n;
  43. /**
  44. * Polymorphic constructor
  45. * As PHP has no support for polymorphic constructors, we hack our own sort of polymorphism using func_num_args, func_get_arg, and gettype. In essence, we're just implementing a simple RTTI filter and calling the appropriate constructor.
  46. * @return
  47. */
  48. function Matrix() {
  49. if( func_num_args() > 0 ) {
  50. $args = func_get_args();
  51. $match = implode(",", array_map('gettype', $args));
  52. switch( $match ) {
  53. //Square matrix - n x n
  54. case 'integer':
  55. $this->m = $args[0];
  56. $this->n = $args[0];
  57. $this->A = array_fill(0, $this->m, array_fill(0, $this->n, 0));
  58. break;
  59. //Rectangular matrix - m x n
  60. case 'integer,integer':
  61. $this->m = $args[0];
  62. $this->n = $args[1];
  63. $this->A = array_fill(0, $this->m, array_fill(0, $this->n, 0));
  64. break;
  65. //Rectangular matrix constant-filled - m x n filled with c
  66. case 'integer,integer,integer':
  67. $this->m = $args[0];
  68. $this->n = $args[1];
  69. $this->A = array_fill(0, $this->m, array_fill(0, $this->n, $args[2]));
  70. break;
  71. //Rectangular matrix constant-filled - m x n filled with c
  72. case 'integer,integer,double':
  73. $this->m = $args[0];
  74. $this->n = $args[1];
  75. $this->A = array_fill(0, $this->m, array_fill(0, $this->n, $args[2]));
  76. break;
  77. //Rectangular matrix - m x n initialized from 2D array
  78. case 'array':
  79. $this->m = count($args[0]);
  80. $this->n = count($args[0][0]);
  81. $this->A = $args[0];
  82. break;
  83. //Rectangular matrix - m x n initialized from 2D array
  84. case 'array,integer,integer':
  85. $this->m = $args[1];
  86. $this->n = $args[2];
  87. $this->A = $args[0];
  88. break;
  89. //Rectangular matrix - m x n initialized from packed array
  90. case 'array,integer':
  91. $this->m = $args[1];
  92. if ($this->m != 0)
  93. $this->n = count($args[0]) / $this->m;
  94. else
  95. $this->n = 0;
  96. if ($this->m * $this->n == count($args[0]))
  97. for($i = 0; $i < $this->m; $i++)
  98. for($j = 0; $j < $this->n; $j++)
  99. $this->A[$i][$j] = $args[0][$i + $j * $this->m];
  100. else
  101. trigger_error(ArrayLengthException, ERROR);
  102. break;
  103. default:
  104. trigger_error(PolymorphicArgumentException, ERROR);
  105. break;
  106. }
  107. } else
  108. trigger_error(PolymorphicArgumentException, ERROR);
  109. }
  110. /**
  111. * getArray
  112. * @return array Matrix array
  113. */
  114. function &getArray() {
  115. return $this->A;
  116. }
  117. /**
  118. * getArrayCopy
  119. * @return array Matrix array copy
  120. */
  121. function getArrayCopy() {
  122. return $this->A;
  123. }
  124. /** Construct a matrix from a copy of a 2-D array.
  125. * @param double A[][] Two-dimensional array of doubles.
  126. * @exception IllegalArgumentException All rows must have the same length
  127. */
  128. function constructWithCopy($A) {
  129. $this->m = count($A);
  130. $this->n = count($A[0]);
  131. $X = new Matrix($this->m, $this->n);
  132. for ($i = 0; $i < $this->m; $i++) {
  133. if (count($A[$i]) != $this->n)
  134. trigger_error(RowLengthException, ERROR);
  135. for ($j = 0; $j < $this->n; $j++)
  136. $X->A[$i][$j] = $A[$i][$j];
  137. }
  138. return $X;
  139. }
  140. /**
  141. * getColumnPacked
  142. * Get a column-packed array
  143. * @return array Column-packed matrix array
  144. */
  145. function getColumnPackedCopy() {
  146. $P = array();
  147. for($i = 0; $i < $this->m; $i++) {
  148. for($j = 0; $j < $this->n; $j++) {
  149. array_push($P, $this->A[$j][$i]);
  150. }
  151. }
  152. return $P;
  153. }
  154. /**
  155. * getRowPacked
  156. * Get a row-packed array
  157. * @return array Row-packed matrix array
  158. */
  159. function getRowPackedCopy() {
  160. $P = array();
  161. for($i = 0; $i < $this->m; $i++) {
  162. for($j = 0; $j < $this->n; $j++) {
  163. array_push($P, $this->A[$i][$j]);
  164. }
  165. }
  166. return $P;
  167. }
  168. /**
  169. * getRowDimension
  170. * @return int Row dimension
  171. */
  172. function getRowDimension() {
  173. return $this->m;
  174. }
  175. /**
  176. * getColumnDimension
  177. * @return int Column dimension
  178. */
  179. function getColumnDimension() {
  180. return $this->n;
  181. }
  182. /**
  183. * get
  184. * Get the i,j-th element of the matrix.
  185. * @param int $i Row position
  186. * @param int $j Column position
  187. * @return mixed Element (int/float/double)
  188. */
  189. function get( $i = null, $j = null ) {
  190. return $this->A[$i][$j];
  191. }
  192. /**
  193. * getMatrix
  194. * Get a submatrix
  195. * @param int $i0 Initial row index
  196. * @param int $iF Final row index
  197. * @param int $j0 Initial column index
  198. * @param int $jF Final column index
  199. * @return Matrix Submatrix
  200. */
  201. function getMatrix() {
  202. if( func_num_args() > 0 ) {
  203. $args = func_get_args();
  204. $match = implode(",", array_map('gettype', $args));
  205. switch( $match ) {
  206. //A($i0...; $j0...)
  207. case 'integer,integer':
  208. list($i0, $j0) = $args;
  209. $m = $i0 >= 0 ? $this->m - $i0 : trigger_error(ArgumentBoundsException, ERROR);
  210. $n = $j0 >= 0 ? $this->n - $j0 : trigger_error(ArgumentBoundsException, ERROR);
  211. $R = new Matrix($m, $n);
  212. for($i = $i0; $i < $this->m; $i++)
  213. for($j = $j0; $j < $this->n; $j++)
  214. $R->set($i, $j, $this->A[$i][$j]);
  215. return $R;
  216. break;
  217. //A($i0...$iF; $j0...$jF)
  218. case 'integer,integer,integer,integer':
  219. list($i0, $iF, $j0, $jF) = $args;
  220. $m = ( ($iF > $i0) && ($this->m >= $iF) && ($i0 >= 0) ) ? $iF - $i0 : trigger_error(ArgumentBoundsException, ERROR);
  221. $n = ( ($jF > $j0) && ($this->n >= $jF) && ($j0 >= 0) ) ? $jF - $j0 : trigger_error(ArgumentBoundsException, ERROR);
  222. $R = new Matrix($m+1, $n+1);
  223. for($i = $i0; $i <= $iF; $i++)
  224. for($j = $j0; $j <= $jF; $j++)
  225. $R->set($i - $i0, $j - $j0, $this->A[$i][$j]);
  226. return $R;
  227. break;
  228. //$R = array of row indices; $C = array of column indices
  229. case 'array,array':
  230. list($RL, $CL) = $args;
  231. $m = count($RL) > 0 ? count($RL) : trigger_error(ArgumentBoundsException, ERROR);
  232. $n = count($CL) > 0 ? count($CL) : trigger_error(ArgumentBoundsException, ERROR);
  233. $R = new Matrix($m, $n);
  234. for($i = 0; $i < $m; $i++)
  235. for($j = 0; $j < $n; $j++)
  236. $R->set($i - $i0, $j - $j0, $this->A[$RL[$i]][$CL[$j]]);
  237. return $R;
  238. break;
  239. //$RL = array of row indices; $CL = array of column indices
  240. case 'array,array':
  241. list($RL, $CL) = $args;
  242. $m = count($RL) > 0 ? count($RL) : trigger_error(ArgumentBoundsException, ERROR);
  243. $n = count($CL) > 0 ? count($CL) : trigger_error(ArgumentBoundsException, ERROR);
  244. $R = new Matrix($m, $n);
  245. for($i = 0; $i < $m; $i++)
  246. for($j = 0; $j < $n; $j++)
  247. $R->set($i, $j, $this->A[$RL[$i]][$CL[$j]]);
  248. return $R;
  249. break;
  250. //A($i0...$iF); $CL = array of column indices
  251. case 'integer,integer,array':
  252. list($i0, $iF, $CL) = $args;
  253. $m = ( ($iF > $i0) && ($this->m >= $iF) && ($i0 >= 0) ) ? $iF - $i0 : trigger_error(ArgumentBoundsException, ERROR);
  254. $n = count($CL) > 0 ? count($CL) : trigger_error(ArgumentBoundsException, ERROR);
  255. $R = new Matrix($m, $n);
  256. for($i = $i0; $i < $iF; $i++)
  257. for($j = 0; $j < $n; $j++)
  258. $R->set($i - $i0, $j, $this->A[$RL[$i]][$j]);
  259. return $R;
  260. break;
  261. //$RL = array of row indices
  262. case 'array,integer,integer':
  263. list($RL, $j0, $jF) = $args;
  264. $m = count($RL) > 0 ? count($RL) : trigger_error(ArgumentBoundsException, ERROR);
  265. $n = ( ($jF >= $j0) && ($this->n >= $jF) && ($j0 >= 0) ) ? $jF - $j0 : trigger_error(ArgumentBoundsException, ERROR);
  266. $R = new Matrix($m, $n+1);
  267. for($i = 0; $i < $m; $i++)
  268. for($j = $j0; $j <= $jF; $j++)
  269. $R->set($i, $j - $j0, $this->A[$RL[$i]][$j]);
  270. return $R;
  271. break;
  272. default:
  273. trigger_error(PolymorphicArgumentException, ERROR);
  274. break;
  275. }
  276. } else {
  277. trigger_error(PolymorphicArgumentException, ERROR);
  278. }
  279. }
  280. /**
  281. * setMatrix
  282. * Set a submatrix
  283. * @param int $i0 Initial row index
  284. * @param int $j0 Initial column index
  285. * @param mixed $S Matrix/Array submatrix
  286. * ($i0, $j0, $S) $S = Matrix
  287. * ($i0, $j0, $S) $S = Array
  288. */
  289. function setMatrix( ) {
  290. if( func_num_args() > 0 ) {
  291. $args = func_get_args();
  292. $match = implode(",", array_map('gettype', $args));
  293. switch( $match ) {
  294. case 'integer,integer,object':
  295. $M = is_a($args[2], 'Matrix') ? $args[2] : trigger_error(ArgumentTypeException, ERROR);
  296. $i0 = ( ($args[0] + $M->m) <= $this->m ) ? $args[0] : trigger_error(ArgumentBoundsException, ERROR);
  297. $j0 = ( ($args[1] + $M->n) <= $this->n ) ? $args[1] : trigger_error(ArgumentBoundsException, ERROR);
  298. for($i = $i0; $i < $i0 + $M->m; $i++) {
  299. for($j = $j0; $j < $j0 + $M->n; $j++) {
  300. $this->A[$i][$j] = $M->get($i - $i0, $j - $j0);
  301. }
  302. }
  303. break;
  304. case 'integer,integer,array':
  305. $M = new Matrix($args[2]);
  306. $i0 = ( ($args[0] + $M->m) <= $this->m ) ? $args[0] : trigger_error(ArgumentBoundsException, ERROR);
  307. $j0 = ( ($args[1] + $M->n) <= $this->n ) ? $args[1] : trigger_error(ArgumentBoundsException, ERROR);
  308. for($i = $i0; $i < $i0 + $M->m; $i++) {
  309. for($j = $j0; $j < $j0 + $M->n; $j++) {
  310. $this->A[$i][$j] = $M->get($i - $i0, $j - $j0);
  311. }
  312. }
  313. break;
  314. default:
  315. trigger_error(PolymorphicArgumentException, ERROR);
  316. break;
  317. }
  318. } else {
  319. trigger_error(PolymorphicArgumentException, ERROR);
  320. }
  321. }
  322. /**
  323. * checkMatrixDimensions
  324. * Is matrix B the same size?
  325. * @param Matrix $B Matrix B
  326. * @return boolean
  327. */
  328. function checkMatrixDimensions( $B = null ) {
  329. if( is_a($B, 'Matrix') )
  330. if( ($this->m == $B->m) && ($this->n == $B->n) )
  331. return true;
  332. else
  333. trigger_error(MatrixDimensionException, ERROR);
  334. else
  335. trigger_error(ArgumentTypeException, ERROR);
  336. }
  337. /**
  338. * set
  339. * Set the i,j-th element of the matrix.
  340. * @param int $i Row position
  341. * @param int $j Column position
  342. * @param mixed $c Int/float/double value
  343. * @return mixed Element (int/float/double)
  344. */
  345. function set( $i = null, $j = null, $c = null ) {
  346. // Optimized set version just has this
  347. $this->A[$i][$j] = $c;
  348. /*
  349. if( is_int($i) && is_int($j) && is_numeric($c) ) {
  350. if( ( $i < $this->m ) && ( $j < $this->n ) ) {
  351. $this->A[$i][$j] = $c;
  352. } else {
  353. echo "A[$i][$j] = $c<br />";
  354. trigger_error(ArgumentBoundsException, WARNING);
  355. }
  356. } else {
  357. trigger_error(ArgumentTypeException, WARNING);
  358. }
  359. */
  360. }
  361. /**
  362. * identity
  363. * Generate an identity matrix.
  364. * @param int $m Row dimension
  365. * @param int $n Column dimension
  366. * @return Matrix Identity matrix
  367. */
  368. function &identity( $m = null, $n = null ) {
  369. return Matrix::diagonal($m, $n, 1);
  370. }
  371. /**
  372. * diagonal
  373. * Generate a diagonal matrix
  374. * @param int $m Row dimension
  375. * @param int $n Column dimension
  376. * @param mixed $c Diagonal value
  377. * @return Matrix Diagonal matrix
  378. */
  379. function &diagonal( $m = null, $n = null, $c = 1 ) {
  380. $R = new Matrix($m, $n);
  381. for($i = 0; $i < $m; $i++)
  382. $R->set($i, $i, $c);
  383. return $R;
  384. }
  385. /**
  386. * filled
  387. * Generate a filled matrix
  388. * @param int $m Row dimension
  389. * @param int $n Column dimension
  390. * @param int $c Fill constant
  391. * @return Matrix Filled matrix
  392. */
  393. function &filled( $m = null, $n = null, $c = 0 ) {
  394. if( is_int($m) && is_int($n) && is_numeric($c) ) {
  395. $R = new Matrix($m, $n, $c);
  396. return $R;
  397. } else {
  398. trigger_error(ArgumentTypeException, ERROR);
  399. }
  400. }
  401. /**
  402. * random
  403. * Generate a random matrix
  404. * @param int $m Row dimension
  405. * @param int $n Column dimension
  406. * @return Matrix Random matrix
  407. */
  408. function &random( $m = null, $n = null, $a = RAND_MIN, $b = RAND_MAX ) {
  409. if( is_int($m) && is_int($n) && is_numeric($a) && is_numeric($b) ) {
  410. $R = new Matrix($m, $n);
  411. for($i = 0; $i < $m; $i++)
  412. for($j = 0; $j < $n; $j++)
  413. $R->set($i, $j, mt_rand($a, $b));
  414. return $R;
  415. } else {
  416. trigger_error(ArgumentTypeException, ERROR);
  417. }
  418. }
  419. /**
  420. * packed
  421. * Alias for getRowPacked
  422. * @return array Packed array
  423. */
  424. function &packed() {
  425. return $this->getRowPacked();
  426. }
  427. /**
  428. * getMatrixByRow
  429. * Get a submatrix by row index/range
  430. * @param int $i0 Initial row index
  431. * @param int $iF Final row index
  432. * @return Matrix Submatrix
  433. */
  434. function getMatrixByRow( $i0 = null, $iF = null ) {
  435. if( is_int($i0) ) {
  436. if( is_int($iF) )
  437. return $this->getMatrix($i0, 0, $iF + 1, $this->n);
  438. else
  439. return $this->getMatrix($i0, 0, $i0 + 1, $this->n);
  440. } else
  441. trigger_error(ArgumentTypeException, ERROR);
  442. }
  443. /**
  444. * getMatrixByCol
  445. * Get a submatrix by column index/range
  446. * @param int $i0 Initial column index
  447. * @param int $iF Final column index
  448. * @return Matrix Submatrix
  449. */
  450. function getMatrixByCol( $j0 = null, $jF = null ) {
  451. if( is_int($j0) ) {
  452. if( is_int($jF) )
  453. return $this->getMatrix(0, $j0, $this->m, $jF + 1);
  454. else
  455. return $this->getMatrix(0, $j0, $this->m, $j0 + 1);
  456. } else
  457. trigger_error(ArgumentTypeException, ERROR);
  458. }
  459. /**
  460. * transpose
  461. * Tranpose matrix
  462. * @return Matrix Transposed matrix
  463. */
  464. function transpose() {
  465. $R = new Matrix($this->n, $this->m);
  466. for($i = 0; $i < $this->m; $i++)
  467. for($j = 0; $j < $this->n; $j++)
  468. $R->set($j, $i, $this->A[$i][$j]);
  469. return $R;
  470. }
  471. /*
  472. public Matrix transpose () {
  473. Matrix X = new Matrix(n,m);
  474. double[][] C = X.getArray();
  475. for (int i = 0; i < m; i++) {
  476. for (int j = 0; j < n; j++) {
  477. C[j][i] = A[i][j];
  478. }
  479. }
  480. return X;
  481. }
  482. */
  483. /**
  484. * norm1
  485. * One norm
  486. * @return float Maximum column sum
  487. */
  488. function norm1() {
  489. $r = 0;
  490. for($j = 0; $j < $this->n; $j++) {
  491. $s = 0;
  492. for($i = 0; $i < $this->m; $i++) {
  493. $s += abs($this->A[$i][$j]);
  494. }
  495. $r = ( $r > $s ) ? $r : $s;
  496. }
  497. return $r;
  498. }
  499. /**
  500. * norm2
  501. * Maximum singular value
  502. * @return float Maximum singular value
  503. */
  504. function norm2() {
  505. }
  506. /**
  507. * normInf
  508. * Infinite norm
  509. * @return float Maximum row sum
  510. */
  511. function normInf() {
  512. $r = 0;
  513. for($i = 0; $i < $this->m; $i++) {
  514. $s = 0;
  515. for($j = 0; $j < $this->n; $j++) {
  516. $s += abs($this->A[$i][$j]);
  517. }
  518. $r = ( $r > $s ) ? $r : $s;
  519. }
  520. return $r;
  521. }
  522. /**
  523. * normF
  524. * Frobenius norm
  525. * @return float Square root of the sum of all elements squared
  526. */
  527. function normF() {
  528. $f = 0;
  529. for ($i = 0; $i < $this->m; $i++)
  530. for ($j = 0; $j < $this->n; $j++)
  531. $f = hypo($f,$this->A[$i][$j]);
  532. return $f;
  533. }
  534. /**
  535. * Matrix rank
  536. * @return effective numerical rank, obtained from SVD.
  537. */
  538. function rank () {
  539. $svd = new SingularValueDecomposition($this);
  540. return $svd->rank();
  541. }
  542. /**
  543. * Matrix condition (2 norm)
  544. * @return ratio of largest to smallest singular value.
  545. */
  546. function cond () {
  547. $svd = new SingularValueDecomposition($this);
  548. return $svd->cond();
  549. }
  550. /**
  551. * trace
  552. * Sum of diagonal elements
  553. * @return float Sum of diagonal elements
  554. */
  555. function trace() {
  556. $s = 0;
  557. $n = min($this->m, $this->n);
  558. for($i = 0; $i < $n; $i++)
  559. $s += $this->A[$i][$i];
  560. return $s;
  561. }
  562. /**
  563. * uminus
  564. * Unary minus matrix -A
  565. * @return Matrix Unary minus matrix
  566. */
  567. function uminus() {
  568. }
  569. /**
  570. * plus
  571. * A + B
  572. * @param mixed $B Matrix/Array
  573. * @return Matrix Sum
  574. */
  575. function plus() {
  576. if( func_num_args() > 0 ) {
  577. $args = func_get_args();
  578. $match = implode(",", array_map('gettype', $args));
  579. switch( $match ) {
  580. case 'object':
  581. $M = is_a($args[0], 'Matrix') ? $args[0] : trigger_error(ArgumentTypeException, ERROR);
  582. //$this->checkMatrixDimensions($M);
  583. for($i = 0; $i < $this->m; $i++) {
  584. for($j = 0; $j < $this->n; $j++) {
  585. $M->set($i, $j, $M->get($i, $j) + $this->A[$i][$j]);
  586. }
  587. }
  588. return $M;
  589. break;
  590. case 'array':
  591. $M = new Matrix($args[0]);
  592. //$this->checkMatrixDimensions($M);
  593. for($i = 0; $i < $this->m; $i++) {
  594. for($j = 0; $j < $this->n; $j++) {
  595. $M->set($i, $j, $M->get($i, $j) + $this->A[$i][$j]);
  596. }
  597. }
  598. return $M;
  599. break;
  600. default:
  601. trigger_error(PolymorphicArgumentException, ERROR);
  602. break;
  603. }
  604. } else {
  605. trigger_error(PolymorphicArgumentException, ERROR);
  606. }
  607. }
  608. /**
  609. * plusEquals
  610. * A = A + B
  611. * @param mixed $B Matrix/Array
  612. * @return Matrix Sum
  613. */
  614. function &plusEquals() {
  615. if( func_num_args() > 0 ) {
  616. $args = func_get_args();
  617. $match = implode(",", array_map('gettype', $args));
  618. switch( $match ) {
  619. case 'object':
  620. $M = is_a($args[0], 'Matrix') ? $args[0] : trigger_error(ArgumentTypeException, ERROR);
  621. $this->checkMatrixDimensions($M);
  622. for($i = 0; $i < $this->m; $i++) {
  623. for($j = 0; $j < $this->n; $j++) {
  624. $this->A[$i][$j] += $M->get($i, $j);
  625. }
  626. }
  627. return $this;
  628. break;
  629. case 'array':
  630. $M = new Matrix($args[0]);
  631. $this->checkMatrixDimensions($M);
  632. for($i = 0; $i < $this->m; $i++) {
  633. for($j = 0; $j < $this->n; $j++) {
  634. $this->A[$i][$j] += $M->get($i, $j);
  635. }
  636. }
  637. return $this;
  638. break;
  639. default:
  640. trigger_error(PolymorphicArgumentException, ERROR);
  641. break;
  642. }
  643. } else {
  644. trigger_error(PolymorphicArgumentException, ERROR);
  645. }
  646. }
  647. /**
  648. * minus
  649. * A - B
  650. * @param mixed $B Matrix/Array
  651. * @return Matrix Sum
  652. */
  653. function minus() {
  654. if( func_num_args() > 0 ) {
  655. $args = func_get_args();
  656. $match = implode(",", array_map('gettype', $args));
  657. switch( $match ) {
  658. case 'object':
  659. $M = is_a($args[0], 'Matrix') ? $args[0] : trigger_error(ArgumentTypeException, ERROR);
  660. $this->checkMatrixDimensions($M);
  661. for($i = 0; $i < $this->m; $i++) {
  662. for($j = 0; $j < $this->n; $j++) {
  663. $M->set($i, $j, $M->get($i, $j) - $this->A[$i][$j]);
  664. }
  665. }
  666. return $M;
  667. break;
  668. case 'array':
  669. $M = new Matrix($args[0]);
  670. $this->checkMatrixDimensions($M);
  671. for($i = 0; $i < $this->m; $i++) {
  672. for($j = 0; $j < $this->n; $j++) {
  673. $M->set($i, $j, $M->get($i, $j) - $this->A[$i][$j]);
  674. }
  675. }
  676. return $M;
  677. break;
  678. default:
  679. trigger_error(PolymorphicArgumentException, ERROR);
  680. break;
  681. }
  682. } else {
  683. trigger_error(PolymorphicArgumentException, ERROR);
  684. }
  685. }
  686. /**
  687. * minusEquals
  688. * A = A - B
  689. * @param mixed $B Matrix/Array
  690. * @return Matrix Sum
  691. */
  692. function &minusEquals() {
  693. if( func_num_args() > 0 ) {
  694. $args = func_get_args();
  695. $match = implode(",", array_map('gettype', $args));
  696. switch( $match ) {
  697. case 'object':
  698. $M = is_a($args[0], 'Matrix') ? $args[0] : trigger_error(ArgumentTypeException, ERROR);
  699. $this->checkMatrixDimensions($M);
  700. for($i = 0; $i < $this->m; $i++) {
  701. for($j = 0; $j < $this->n; $j++) {
  702. $this->A[$i][$j] -= $M->get($i, $j);
  703. }
  704. }
  705. return $this;
  706. break;
  707. case 'array':
  708. $M = new Matrix($args[0]);
  709. $this->checkMatrixDimensions($M);
  710. for($i = 0; $i < $this->m; $i++) {
  711. for($j = 0; $j < $this->n; $j++) {
  712. $this->A[$i][$j] -= $M->get($i, $j);
  713. }
  714. }
  715. return $this;
  716. break;
  717. default:
  718. trigger_error(PolymorphicArgumentException, ERROR);
  719. break;
  720. }
  721. } else {
  722. trigger_error(PolymorphicArgumentException, ERROR);
  723. }
  724. }
  725. /**
  726. * arrayTimes
  727. * Element-by-element multiplication
  728. * Cij = Aij * Bij
  729. * @param mixed $B Matrix/Array
  730. * @return Matrix Matrix Cij
  731. */
  732. function arrayTimes() {
  733. if( func_num_args() > 0 ) {
  734. $args = func_get_args();
  735. $match = implode(",", array_map('gettype', $args));
  736. switch( $match ) {
  737. case 'object':
  738. $M = is_a($args[0], 'Matrix') ? $args[0] : trigger_error(ArgumentTypeException, ERROR);
  739. $this->checkMatrixDimensions($M);
  740. for($i = 0; $i < $this->m; $i++) {
  741. for($j = 0; $j < $this->n; $j++) {
  742. $M->set($i, $j, $M->get($i, $j) * $this->A[$i][$j]);
  743. }
  744. }
  745. return $M;
  746. break;
  747. case 'array':
  748. $M = new Matrix($args[0]);
  749. $this->checkMatrixDimensions($M);
  750. for($i = 0; $i < $this->m; $i++) {
  751. for($j = 0; $j < $this->n; $j++) {
  752. $M->set($i, $j, $M->get($i, $j) * $this->A[$i][$j]);
  753. }
  754. }
  755. return $M;
  756. break;
  757. default:
  758. trigger_error(PolymorphicArgumentException, ERROR);
  759. break;
  760. }
  761. } else {
  762. trigger_error(PolymorphicArgumentException, ERROR);
  763. }
  764. }
  765. /**
  766. * arrayTimesEquals
  767. * Element-by-element multiplication
  768. * Aij = Aij * Bij
  769. * @param mixed $B Matrix/Array
  770. * @return Matrix Matrix Aij
  771. */
  772. function &arrayTimesEquals() {
  773. if( func_num_args() > 0 ) {
  774. $args = func_get_args();
  775. $match = implode(",", array_map('gettype', $args));
  776. switch( $match ) {
  777. case 'object':
  778. $M = is_a($args[0], 'Matrix') ? $args[0] : trigger_error(ArgumentTypeException, ERROR);
  779. $this->checkMatrixDimensions($M);
  780. for($i = 0; $i < $this->m; $i++) {
  781. for($j = 0; $j < $this->n; $j++) {
  782. $this->A[$i][$j] *= $M->get($i, $j);
  783. }
  784. }
  785. return $this;
  786. break;
  787. case 'array':
  788. $M = new Matrix($args[0]);
  789. $this->checkMatrixDimensions($M);
  790. for($i = 0; $i < $this->m; $i++) {
  791. for($j = 0; $j < $this->n; $j++) {
  792. $this->A[$i][$j] *= $M->get($i, $j);
  793. }
  794. }
  795. return $this;
  796. break;
  797. default:
  798. trigger_error(PolymorphicArgumentException, ERROR);
  799. break;
  800. }
  801. } else {
  802. trigger_error(PolymorphicArgumentException, ERROR);
  803. }
  804. }
  805. /**
  806. * arrayRightDivide
  807. * Element-by-element right division
  808. * A / B
  809. * @param Matrix $B Matrix B
  810. * @return Matrix Division result
  811. */
  812. function arrayRightDivide() {
  813. if( func_num_args() > 0 ) {
  814. $args = func_get_args();
  815. $match = implode(",", array_map('gettype', $args));
  816. switch( $match ) {
  817. case 'object':
  818. $M = is_a($args[0], 'Matrix') ? $args[0] : trigger_error(ArgumentTypeException, ERROR);
  819. $this->checkMatrixDimensions($M);
  820. for($i = 0; $i < $this->m; $i++) {
  821. for($j = 0; $j < $this->n; $j++) {
  822. $M->set($i, $j, $this->A[$i][$j] / $M->get($i, $j) );
  823. }
  824. }
  825. return $M;
  826. break;
  827. case 'array':
  828. $M = new Matrix($args[0]);
  829. $this->checkMatrixDimensions($M);
  830. for($i = 0; $i < $this->m; $i++) {
  831. for($j = 0; $j < $this->n; $j++) {
  832. $M->set($i, $j, $this->A[$i][$j] / $M->get($i, $j));
  833. }
  834. }
  835. return $M;
  836. break;
  837. default:
  838. trigger_error(PolymorphicArgumentException, ERROR);
  839. break;
  840. }
  841. } else {
  842. trigger_error(PolymorphicArgumentException, ERROR);
  843. }
  844. }
  845. /**
  846. * arrayRightDivideEquals
  847. * Element-by-element right division
  848. * Aij = Aij / Bij
  849. * @param mixed $B Matrix/Array
  850. * @return Matrix Matrix Aij
  851. */
  852. function &arrayRightDivideEquals() {
  853. if( func_num_args() > 0 ) {
  854. $args = func_get_args();
  855. $match = implode(",", array_map('gettype', $args));
  856. switch( $match ) {
  857. case 'object':
  858. $M = is_a($args[0], 'Matrix') ? $args[0] : trigger_error(ArgumentTypeException, ERROR);
  859. $this->checkMatrixDimensions($M);
  860. for($i = 0; $i < $this->m; $i++) {
  861. for($j = 0; $j < $this->n; $j++) {
  862. $this->A[$i][$j] = $this->A[$i][$j] / $M->get($i, $j);
  863. }
  864. }
  865. return $M;
  866. break;
  867. case 'array':
  868. $M = new Matrix($args[0]);
  869. $this->checkMatrixDimensions($M);
  870. for($i = 0; $i < $this->m; $i++) {
  871. for($j = 0; $j < $this->n; $j++) {
  872. $this->A[$i][$j] = $this->A[$i][$j] / $M->get($i, $j);
  873. }
  874. }
  875. return $M;
  876. break;
  877. default:
  878. trigger_error(PolymorphicArgumentException, ERROR);
  879. break;
  880. }
  881. } else {
  882. trigger_error(PolymorphicArgumentException, ERROR);
  883. }
  884. }
  885. /**
  886. * arrayLeftDivide
  887. * Element-by-element Left division
  888. * A / B
  889. * @param Matrix $B Matrix B
  890. * @return Matrix Division result
  891. */
  892. function arrayLeftDivide() {
  893. if( func_num_args() > 0 ) {
  894. $args = func_get_args();
  895. $match = implode(",", array_map('gettype', $args));
  896. switch( $match ) {
  897. case 'object':
  898. $M = is_a($args[0], 'Matrix') ? $args[0] : trigger_error(ArgumentTypeException, ERROR);
  899. $this->checkMatrixDimensions($M);
  900. for($i = 0; $i < $this->m; $i++) {
  901. for($j = 0; $j < $this->n; $j++) {
  902. $M->set($i, $j, $M->get($i, $j) / $this->A[$i][$j] );
  903. }
  904. }
  905. return $M;
  906. break;
  907. case 'array':
  908. $M = new Matrix($args[0]);
  909. $this->checkMatrixDimensions($M);
  910. for($i = 0; $i < $this->m; $i++) {
  911. for($j = 0; $j < $this->n; $j++) {
  912. $M->set($i, $j, $M->get($i, $j) / $this->A[$i][$j] );
  913. }
  914. }
  915. return $M;
  916. break;
  917. default:
  918. trigger_error(PolymorphicArgumentException, ERROR);
  919. break;
  920. }
  921. } else {
  922. trigger_error(PolymorphicArgumentException, ERROR);
  923. }
  924. }
  925. /**
  926. * arrayLeftDivideEquals
  927. * Element-by-element Left division
  928. * Aij = Aij / Bij
  929. * @param mixed $B Matrix/Array
  930. * @return Matrix Matrix Aij
  931. */
  932. function &arrayLeftDivideEquals() {
  933. if( func_num_args() > 0 ) {
  934. $args = func_get_args();
  935. $match = implode(",", array_map('gettype', $args));
  936. switch( $match ) {
  937. case 'object':
  938. $M = is_a($args[0], 'Matrix') ? $args[0] : trigger_error(ArgumentTypeException, ERROR);
  939. $this->checkMatrixDimensions($M);
  940. for($i = 0; $i < $this->m; $i++) {
  941. for($j = 0; $j < $this->n; $j++) {
  942. $this->A[$i][$j] = $M->get($i, $j) / $this->A[$i][$j];
  943. }
  944. }
  945. return $M;
  946. break;
  947. case 'array':
  948. $M = new Matrix($args[0]);
  949. $this->checkMatrixDimensions($M);
  950. for($i = 0; $i < $this->m; $i++) {
  951. for($j = 0; $j < $this->n; $j++) {
  952. $this->A[$i][$j] = $M->get($i, $j) / $this->A[$i][$j];
  953. }
  954. }
  955. return $M;
  956. break;
  957. default:
  958. trigger_error(PolymorphicArgumentException, ERROR);
  959. break;
  960. }
  961. } else {
  962. trigger_error(PolymorphicArgumentException, ERROR);
  963. }
  964. }
  965. /**
  966. * times
  967. * Matrix multiplication
  968. * @param mixed $n Matrix/Array/Scalar
  969. * @return Matrix Product
  970. */
  971. function times() {
  972. if(func_num_args() > 0) {
  973. $args = func_get_args();
  974. $match = implode(",", array_map('gettype', $args));
  975. switch($match) {
  976. case 'object':
  977. $B = is_a($args[0], 'Matrix') ? $args[0] : trigger_error(ArgumentTypeException, ERROR);
  978. if($this->n == $B->m) {
  979. $C = new Matrix($this->m, $B->n);
  980. for($j = 0; $j < $B->n; $j++ ) {
  981. for ($k = 0; $k < $this->n; $k++)
  982. $Bcolj[$k] = $B->A[$k][$j];
  983. for($i = 0; $i < $this->m; $i++ ) {
  984. $Arowi = $this->A[$i];
  985. $s = 0;
  986. for( $k = 0; $k < $this->n; $k++ )
  987. $s += $Arowi[$k] * $Bcolj[$k];
  988. $C->A[$i][$j] = $s;
  989. }
  990. }
  991. return $C;
  992. } else
  993. trigger_error(MatrixDimensionMismatch, FATAL);
  994. break;
  995. case 'array':
  996. $B = new Matrix($args[0]);
  997. if($this->n == $B->m) {
  998. $C = new Matrix($this->m, $B->n);
  999. for($i = 0; $i < $C->m; $i++) {
  1000. for($j = 0; $j < $C->n; $j++) {
  1001. $s = "0";
  1002. for($k = 0; $k < $C->n; $k++)
  1003. $s += $this->A[$i][$k] * $B->A[$k][$j];
  1004. $C->A[$i][$j] = $s;
  1005. }
  1006. }
  1007. return $C;
  1008. } else
  1009. trigger_error(MatrixDimensionMismatch, FATAL);
  1010. return $M;
  1011. break;
  1012. case 'integer':
  1013. $C = new Matrix($this->A);
  1014. for($i = 0; $i < $C->m; $i++)
  1015. for($j = 0; $j < $C->n; $j++)
  1016. $C->A[$i][$j] *= $args[0];
  1017. return $C;
  1018. break;
  1019. case 'double':
  1020. $C = new Matrix($this->m, $this->n);
  1021. for($i = 0; $i < $C->m; $i++)
  1022. for($j = 0; $j < $C->n; $j++)
  1023. $C->A[$i][$j] = $args[0] * $this->A[$i][$j];
  1024. return $C;
  1025. break;
  1026. case 'float':
  1027. $C = new Matrix($this->A);
  1028. for($i = 0; $i < $C->m; $i++)
  1029. for($j = 0; $j < $C->n; $j++)
  1030. $C->A[$i][$j] *= $args[0];
  1031. return $C;
  1032. break;
  1033. default:
  1034. trigger_error(PolymorphicArgumentException, ERROR);
  1035. break;
  1036. }
  1037. } else
  1038. trigger_error(PolymorphicArgumentException, ERROR);
  1039. }
  1040. /**
  1041. * chol
  1042. * Cholesky decomposition
  1043. * @return Matrix Cholesky decomposition
  1044. */
  1045. function chol() {
  1046. return new CholeskyDecomposition($this);
  1047. }
  1048. /**
  1049. * lu
  1050. * LU decomposition
  1051. * @return Matrix LU decomposition
  1052. */
  1053. function lu() {
  1054. return new LUDecomposition($this);
  1055. }
  1056. /**
  1057. * qr
  1058. * QR decomposition
  1059. * @return Matrix QR decomposition
  1060. */
  1061. function qr() {
  1062. return new QRDecomposition($this);
  1063. }
  1064. /**
  1065. * eig
  1066. * Eigenvalue decomposition
  1067. * @return Matrix Eigenvalue decomposition
  1068. */
  1069. function eig() {
  1070. return new EigenvalueDecomposition($this);
  1071. }
  1072. /**
  1073. * svd
  1074. * Singular value decomposition
  1075. * @return Singular value decomposition
  1076. */
  1077. function svd() {
  1078. return new SingularValueDecomposition($this);
  1079. }
  1080. /**
  1081. * Solve A*X = B.
  1082. * @param Matrix $B Right hand side
  1083. * @return Matrix ... Solution if A is square, least squares solution otherwise
  1084. */
  1085. function solve($B) {
  1086. if ($this->m == $this->n) {
  1087. $LU = new LUDecomposition($this);
  1088. return $LU->solve($B);
  1089. } else {
  1090. $QR = new QRDecomposition($this);
  1091. return $QR->solve($B);
  1092. }
  1093. }
  1094. /**
  1095. * Matrix inverse or pseudoinverse.
  1096. * @return Matrix ... Inverse(A) if A is square, pseudoinverse otherwise.
  1097. */
  1098. function inverse() {
  1099. return $this->solve($this->identity($this->m, $this->m));
  1100. }
  1101. /**
  1102. * det
  1103. * Calculate determinant
  1104. * @return float Determinant
  1105. */
  1106. function det() {
  1107. $L = new LUDecomposition($this);
  1108. return $L->det();
  1109. }
  1110. /**
  1111. * Older debugging utility for backwards compatability.
  1112. * @return html version of matrix
  1113. */
  1114. function mprint($A, $format="%01.2f", $width=2) {
  1115. $spacing = "";
  1116. $m = count($A);
  1117. $n = count($A[0]);
  1118. for($i = 0; $i < $width; $i++)
  1119. $spacing .= "&nbsp;";
  1120. for ($i = 0; $i < $m; $i++) {
  1121. for ($j = 0; $j < $n; $j++) {
  1122. $formatted = sprintf($format, $A[$i][$j]);
  1123. echo $formatted . $spacing;
  1124. }
  1125. echo "<br />";
  1126. }
  1127. }
  1128. /**
  1129. * Debugging utility.
  1130. * @return Output HTML representation of matrix
  1131. */
  1132. function toHTML($width=2) {
  1133. print( '<table style="background-color:#eee;">');
  1134. for( $i = 0; $i < $this->m; $i++ ) {
  1135. print( '<tr>' );
  1136. for( $j = 0; $j < $this->n; $j++ )
  1137. print( '<td style="background-color:#fff;border:1px solid #000;padding:2px;text-align:center;vertical-align:middle;">' . $this->A[$i][$j] . '</td>' );
  1138. print( '</tr>');
  1139. }
  1140. print( '</table>' );
  1141. }
  1142. }
  1143. ?>