For God so loved the world, that He gave His only begotten Son, that all who believe in Him should not perish but have everlasting life
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.

257 lines
7.9 KiB

  1. <?php
  2. /**
  3. * @package JAMA
  4. *
  5. * For an m-by-n matrix A with m >= n, the LU decomposition is an m-by-n
  6. * unit lower triangular matrix L, an n-by-n upper triangular matrix U,
  7. * and a permutation vector piv of length m so that A(piv,:) = L*U.
  8. * If m < n, then L is m-by-m and U is m-by-n.
  9. *
  10. * The LU decompostion with pivoting always exists, even if the matrix is
  11. * singular, so the constructor will never fail. The primary use of the
  12. * LU decomposition is in the solution of square systems of simultaneous
  13. * linear equations. This will fail if isNonsingular() returns false.
  14. *
  15. * @author Paul Meagher
  16. * @author Bartosz Matosiuk
  17. * @author Michael Bommarito
  18. * @version 1.1
  19. * @license PHP v3.0
  20. */
  21. class PHPExcel_Shared_JAMA_LUDecomposition
  22. {
  23. const MATRIX_SINGULAR_EXCEPTION = "Can only perform operation on singular matrix.";
  24. const MATRIX_SQUARE_EXCEPTION = "Mismatched Row dimension";
  25. /**
  26. * Decomposition storage
  27. * @var array
  28. */
  29. private $LU = array();
  30. /**
  31. * Row dimension.
  32. * @var int
  33. */
  34. private $m;
  35. /**
  36. * Column dimension.
  37. * @var int
  38. */
  39. private $n;
  40. /**
  41. * Pivot sign.
  42. * @var int
  43. */
  44. private $pivsign;
  45. /**
  46. * Internal storage of pivot vector.
  47. * @var array
  48. */
  49. private $piv = array();
  50. /**
  51. * LU Decomposition constructor.
  52. *
  53. * @param $A Rectangular matrix
  54. * @return Structure to access L, U and piv.
  55. */
  56. public function __construct($A)
  57. {
  58. if ($A instanceof PHPExcel_Shared_JAMA_Matrix) {
  59. // Use a "left-looking", dot-product, Crout/Doolittle algorithm.
  60. $this->LU = $A->getArray();
  61. $this->m = $A->getRowDimension();
  62. $this->n = $A->getColumnDimension();
  63. for ($i = 0; $i < $this->m; ++$i) {
  64. $this->piv[$i] = $i;
  65. }
  66. $this->pivsign = 1;
  67. $LUrowi = $LUcolj = array();
  68. // Outer loop.
  69. for ($j = 0; $j < $this->n; ++$j) {
  70. // Make a copy of the j-th column to localize references.
  71. for ($i = 0; $i < $this->m; ++$i) {
  72. $LUcolj[$i] = &$this->LU[$i][$j];
  73. }
  74. // Apply previous transformations.
  75. for ($i = 0; $i < $this->m; ++$i) {
  76. $LUrowi = $this->LU[$i];
  77. // Most of the time is spent in the following dot product.
  78. $kmax = min($i, $j);
  79. $s = 0.0;
  80. for ($k = 0; $k < $kmax; ++$k) {
  81. $s += $LUrowi[$k] * $LUcolj[$k];
  82. }
  83. $LUrowi[$j] = $LUcolj[$i] -= $s;
  84. }
  85. // Find pivot and exchange if necessary.
  86. $p = $j;
  87. for ($i = $j+1; $i < $this->m; ++$i) {
  88. if (abs($LUcolj[$i]) > abs($LUcolj[$p])) {
  89. $p = $i;
  90. }
  91. }
  92. if ($p != $j) {
  93. for ($k = 0; $k < $this->n; ++$k) {
  94. $t = $this->LU[$p][$k];
  95. $this->LU[$p][$k] = $this->LU[$j][$k];
  96. $this->LU[$j][$k] = $t;
  97. }
  98. $k = $this->piv[$p];
  99. $this->piv[$p] = $this->piv[$j];
  100. $this->piv[$j] = $k;
  101. $this->pivsign = $this->pivsign * -1;
  102. }
  103. // Compute multipliers.
  104. if (($j < $this->m) && ($this->LU[$j][$j] != 0.0)) {
  105. for ($i = $j+1; $i < $this->m; ++$i) {
  106. $this->LU[$i][$j] /= $this->LU[$j][$j];
  107. }
  108. }
  109. }
  110. } else {
  111. throw new PHPExcel_Calculation_Exception(PHPExcel_Shared_JAMA_Matrix::ARGUMENT_TYPE_EXCEPTION);
  112. }
  113. } // function __construct()
  114. /**
  115. * Get lower triangular factor.
  116. *
  117. * @return array Lower triangular factor
  118. */
  119. public function getL()
  120. {
  121. for ($i = 0; $i < $this->m; ++$i) {
  122. for ($j = 0; $j < $this->n; ++$j) {
  123. if ($i > $j) {
  124. $L[$i][$j] = $this->LU[$i][$j];
  125. } elseif ($i == $j) {
  126. $L[$i][$j] = 1.0;
  127. } else {
  128. $L[$i][$j] = 0.0;
  129. }
  130. }
  131. }
  132. return new PHPExcel_Shared_JAMA_Matrix($L);
  133. } // function getL()
  134. /**
  135. * Get upper triangular factor.
  136. *
  137. * @return array Upper triangular factor
  138. */
  139. public function getU()
  140. {
  141. for ($i = 0; $i < $this->n; ++$i) {
  142. for ($j = 0; $j < $this->n; ++$j) {
  143. if ($i <= $j) {
  144. $U[$i][$j] = $this->LU[$i][$j];
  145. } else {
  146. $U[$i][$j] = 0.0;
  147. }
  148. }
  149. }
  150. return new PHPExcel_Shared_JAMA_Matrix($U);
  151. } // function getU()
  152. /**
  153. * Return pivot permutation vector.
  154. *
  155. * @return array Pivot vector
  156. */
  157. public function getPivot()
  158. {
  159. return $this->piv;
  160. } // function getPivot()
  161. /**
  162. * Alias for getPivot
  163. *
  164. * @see getPivot
  165. */
  166. public function getDoublePivot()
  167. {
  168. return $this->getPivot();
  169. } // function getDoublePivot()
  170. /**
  171. * Is the matrix nonsingular?
  172. *
  173. * @return true if U, and hence A, is nonsingular.
  174. */
  175. public function isNonsingular()
  176. {
  177. for ($j = 0; $j < $this->n; ++$j) {
  178. if ($this->LU[$j][$j] == 0) {
  179. return false;
  180. }
  181. }
  182. return true;
  183. } // function isNonsingular()
  184. /**
  185. * Count determinants
  186. *
  187. * @return array d matrix deterninat
  188. */
  189. public function det()
  190. {
  191. if ($this->m == $this->n) {
  192. $d = $this->pivsign;
  193. for ($j = 0; $j < $this->n; ++$j) {
  194. $d *= $this->LU[$j][$j];
  195. }
  196. return $d;
  197. } else {
  198. throw new PHPExcel_Calculation_Exception(PHPExcel_Shared_JAMA_Matrix::MATRIX_DIMENSION_EXCEPTION);
  199. }
  200. } // function det()
  201. /**
  202. * Solve A*X = B
  203. *
  204. * @param $B A Matrix with as many rows as A and any number of columns.
  205. * @return X so that L*U*X = B(piv,:)
  206. * @PHPExcel_Calculation_Exception IllegalArgumentException Matrix row dimensions must agree.
  207. * @PHPExcel_Calculation_Exception RuntimeException Matrix is singular.
  208. */
  209. public function solve($B)
  210. {
  211. if ($B->getRowDimension() == $this->m) {
  212. if ($this->isNonsingular()) {
  213. // Copy right hand side with pivoting
  214. $nx = $B->getColumnDimension();
  215. $X = $B->getMatrix($this->piv, 0, $nx-1);
  216. // Solve L*Y = B(piv,:)
  217. for ($k = 0; $k < $this->n; ++$k) {
  218. for ($i = $k+1; $i < $this->n; ++$i) {
  219. for ($j = 0; $j < $nx; ++$j) {
  220. $X->A[$i][$j] -= $X->A[$k][$j] * $this->LU[$i][$k];
  221. }
  222. }
  223. }
  224. // Solve U*X = Y;
  225. for ($k = $this->n-1; $k >= 0; --$k) {
  226. for ($j = 0; $j < $nx; ++$j) {
  227. $X->A[$k][$j] /= $this->LU[$k][$k];
  228. }
  229. for ($i = 0; $i < $k; ++$i) {
  230. for ($j = 0; $j < $nx; ++$j) {
  231. $X->A[$i][$j] -= $X->A[$k][$j] * $this->LU[$i][$k];
  232. }
  233. }
  234. }
  235. return $X;
  236. } else {
  237. throw new PHPExcel_Calculation_Exception(self::MATRIX_SINGULAR_EXCEPTION);
  238. }
  239. } else {
  240. throw new PHPExcel_Calculation_Exception(self::MATRIX_SQUARE_EXCEPTION);
  241. }
  242. }
  243. }