QRDecomposition.php 7.3 KB

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