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.

235 lines
7.5 KiB

  1. <?php
  2. /**
  3. * @package JAMA
  4. *
  5. * For an m-by-n matrix A with m >= n, the QR decomposition is an m-by-n
  6. * orthogonal matrix Q and an n-by-n upper triangular matrix R so that
  7. * A = Q*R.
  8. *
  9. * The QR decompostion always exists, even if the matrix does not have
  10. * full rank, so the constructor will never fail. The primary use of the
  11. * QR decomposition is in the least squares solution of nonsquare systems
  12. * of simultaneous linear equations. This will fail if isFullRank()
  13. * returns false.
  14. *
  15. * @author Paul Meagher
  16. * @license PHP v3.0
  17. * @version 1.1
  18. */
  19. class PHPExcel_Shared_JAMA_QRDecomposition
  20. {
  21. const MATRIX_RANK_EXCEPTION = "Can only perform operation on full-rank matrix.";
  22. /**
  23. * Array for internal storage of decomposition.
  24. * @var array
  25. */
  26. private $QR = array();
  27. /**
  28. * Row dimension.
  29. * @var integer
  30. */
  31. private $m;
  32. /**
  33. * Column dimension.
  34. * @var integer
  35. */
  36. private $n;
  37. /**
  38. * Array for internal storage of diagonal of R.
  39. * @var array
  40. */
  41. private $Rdiag = array();
  42. /**
  43. * QR Decomposition computed by Householder reflections.
  44. *
  45. * @param matrix $A Rectangular matrix
  46. * @return Structure to access R and the Householder vectors and compute Q.
  47. */
  48. public function __construct($A)
  49. {
  50. if ($A instanceof PHPExcel_Shared_JAMA_Matrix) {
  51. // Initialize.
  52. $this->QR = $A->getArrayCopy();
  53. $this->m = $A->getRowDimension();
  54. $this->n = $A->getColumnDimension();
  55. // Main loop.
  56. for ($k = 0; $k < $this->n; ++$k) {
  57. // Compute 2-norm of k-th column without under/overflow.
  58. $nrm = 0.0;
  59. for ($i = $k; $i < $this->m; ++$i) {
  60. $nrm = hypo($nrm, $this->QR[$i][$k]);
  61. }
  62. if ($nrm != 0.0) {
  63. // Form k-th Householder vector.
  64. if ($this->QR[$k][$k] < 0) {
  65. $nrm = -$nrm;
  66. }
  67. for ($i = $k; $i < $this->m; ++$i) {
  68. $this->QR[$i][$k] /= $nrm;
  69. }
  70. $this->QR[$k][$k] += 1.0;
  71. // Apply transformation to remaining columns.
  72. for ($j = $k+1; $j < $this->n; ++$j) {
  73. $s = 0.0;
  74. for ($i = $k; $i < $this->m; ++$i) {
  75. $s += $this->QR[$i][$k] * $this->QR[$i][$j];
  76. }
  77. $s = -$s/$this->QR[$k][$k];
  78. for ($i = $k; $i < $this->m; ++$i) {
  79. $this->QR[$i][$j] += $s * $this->QR[$i][$k];
  80. }
  81. }
  82. }
  83. $this->Rdiag[$k] = -$nrm;
  84. }
  85. } else {
  86. throw new PHPExcel_Calculation_Exception(PHPExcel_Shared_JAMA_Matrix::ARGUMENT_TYPE_EXCEPTION);
  87. }
  88. } // function __construct()
  89. /**
  90. * Is the matrix full rank?
  91. *
  92. * @return boolean true if R, and hence A, has full rank, else false.
  93. */
  94. public function isFullRank()
  95. {
  96. for ($j = 0; $j < $this->n; ++$j) {
  97. if ($this->Rdiag[$j] == 0) {
  98. return false;
  99. }
  100. }
  101. return true;
  102. } // function isFullRank()
  103. /**
  104. * Return the Householder vectors
  105. *
  106. * @return Matrix Lower trapezoidal matrix whose columns define the reflections
  107. */
  108. public function getH()
  109. {
  110. for ($i = 0; $i < $this->m; ++$i) {
  111. for ($j = 0; $j < $this->n; ++$j) {
  112. if ($i >= $j) {
  113. $H[$i][$j] = $this->QR[$i][$j];
  114. } else {
  115. $H[$i][$j] = 0.0;
  116. }
  117. }
  118. }
  119. return new PHPExcel_Shared_JAMA_Matrix($H);
  120. } // function getH()
  121. /**
  122. * Return the upper triangular factor
  123. *
  124. * @return Matrix upper triangular factor
  125. */
  126. public function getR()
  127. {
  128. for ($i = 0; $i < $this->n; ++$i) {
  129. for ($j = 0; $j < $this->n; ++$j) {
  130. if ($i < $j) {
  131. $R[$i][$j] = $this->QR[$i][$j];
  132. } elseif ($i == $j) {
  133. $R[$i][$j] = $this->Rdiag[$i];
  134. } else {
  135. $R[$i][$j] = 0.0;
  136. }
  137. }
  138. }
  139. return new PHPExcel_Shared_JAMA_Matrix($R);
  140. } // function getR()
  141. /**
  142. * Generate and return the (economy-sized) orthogonal factor
  143. *
  144. * @return Matrix orthogonal factor
  145. */
  146. public function getQ()
  147. {
  148. for ($k = $this->n-1; $k >= 0; --$k) {
  149. for ($i = 0; $i < $this->m; ++$i) {
  150. $Q[$i][$k] = 0.0;
  151. }
  152. $Q[$k][$k] = 1.0;
  153. for ($j = $k; $j < $this->n; ++$j) {
  154. if ($this->QR[$k][$k] != 0) {
  155. $s = 0.0;
  156. for ($i = $k; $i < $this->m; ++$i) {
  157. $s += $this->QR[$i][$k] * $Q[$i][$j];
  158. }
  159. $s = -$s/$this->QR[$k][$k];
  160. for ($i = $k; $i < $this->m; ++$i) {
  161. $Q[$i][$j] += $s * $this->QR[$i][$k];
  162. }
  163. }
  164. }
  165. }
  166. /*
  167. for($i = 0; $i < count($Q); ++$i) {
  168. for($j = 0; $j < count($Q); ++$j) {
  169. if (! isset($Q[$i][$j]) ) {
  170. $Q[$i][$j] = 0;
  171. }
  172. }
  173. }
  174. */
  175. return new PHPExcel_Shared_JAMA_Matrix($Q);
  176. } // function getQ()
  177. /**
  178. * Least squares solution of A*X = B
  179. *
  180. * @param Matrix $B A Matrix with as many rows as A and any number of columns.
  181. * @return Matrix Matrix that minimizes the two norm of Q*R*X-B.
  182. */
  183. public function solve($B)
  184. {
  185. if ($B->getRowDimension() == $this->m) {
  186. if ($this->isFullRank()) {
  187. // Copy right hand side
  188. $nx = $B->getColumnDimension();
  189. $X = $B->getArrayCopy();
  190. // Compute Y = transpose(Q)*B
  191. for ($k = 0; $k < $this->n; ++$k) {
  192. for ($j = 0; $j < $nx; ++$j) {
  193. $s = 0.0;
  194. for ($i = $k; $i < $this->m; ++$i) {
  195. $s += $this->QR[$i][$k] * $X[$i][$j];
  196. }
  197. $s = -$s/$this->QR[$k][$k];
  198. for ($i = $k; $i < $this->m; ++$i) {
  199. $X[$i][$j] += $s * $this->QR[$i][$k];
  200. }
  201. }
  202. }
  203. // Solve R*X = Y;
  204. for ($k = $this->n-1; $k >= 0; --$k) {
  205. for ($j = 0; $j < $nx; ++$j) {
  206. $X[$k][$j] /= $this->Rdiag[$k];
  207. }
  208. for ($i = 0; $i < $k; ++$i) {
  209. for ($j = 0; $j < $nx; ++$j) {
  210. $X[$i][$j] -= $X[$k][$j]* $this->QR[$i][$k];
  211. }
  212. }
  213. }
  214. $X = new PHPExcel_Shared_JAMA_Matrix($X);
  215. return ($X->getMatrix(0, $this->n-1, 0, $nx));
  216. } else {
  217. throw new PHPExcel_Calculation_Exception(self::MATRIX_RANK_EXCEPTION);
  218. }
  219. } else {
  220. throw new PHPExcel_Calculation_Exception(PHPExcel_Shared_JAMA_Matrix::MATRIX_DIMENSION_EXCEPTION);
  221. }
  222. }
  223. }