SingularValueDecomposition.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528
  1. <?php
  2. namespace PhpOffice\PhpSpreadsheet\Shared\JAMA;
  3. /**
  4. * For an m-by-n matrix A with m >= n, the singular value decomposition is
  5. * an m-by-n orthogonal matrix U, an n-by-n diagonal matrix S, and
  6. * an n-by-n orthogonal matrix V so that A = U*S*V'.
  7. *
  8. * The singular values, sigma[$k] = S[$k][$k], are ordered so that
  9. * sigma[0] >= sigma[1] >= ... >= sigma[n-1].
  10. *
  11. * The singular value decompostion always exists, so the constructor will
  12. * never fail. The matrix condition number and the effective numerical
  13. * rank can be computed from this decomposition.
  14. *
  15. * @author Paul Meagher
  16. *
  17. * @version 1.1
  18. */
  19. class SingularValueDecomposition
  20. {
  21. /**
  22. * Internal storage of U.
  23. *
  24. * @var array
  25. */
  26. private $U = [];
  27. /**
  28. * Internal storage of V.
  29. *
  30. * @var array
  31. */
  32. private $V = [];
  33. /**
  34. * Internal storage of singular values.
  35. *
  36. * @var array
  37. */
  38. private $s = [];
  39. /**
  40. * Row dimension.
  41. *
  42. * @var int
  43. */
  44. private $m;
  45. /**
  46. * Column dimension.
  47. *
  48. * @var int
  49. */
  50. private $n;
  51. /**
  52. * Construct the singular value decomposition.
  53. *
  54. * Derived from LINPACK code.
  55. *
  56. * @param mixed $Arg Rectangular matrix
  57. */
  58. public function __construct($Arg)
  59. {
  60. // Initialize.
  61. $A = $Arg->getArrayCopy();
  62. $this->m = $Arg->getRowDimension();
  63. $this->n = $Arg->getColumnDimension();
  64. $nu = min($this->m, $this->n);
  65. $e = [];
  66. $work = [];
  67. $wantu = true;
  68. $wantv = true;
  69. $nct = min($this->m - 1, $this->n);
  70. $nrt = max(0, min($this->n - 2, $this->m));
  71. // Reduce A to bidiagonal form, storing the diagonal elements
  72. // in s and the super-diagonal elements in e.
  73. $kMax = max($nct, $nrt);
  74. for ($k = 0; $k < $kMax; ++$k) {
  75. if ($k < $nct) {
  76. // Compute the transformation for the k-th column and
  77. // place the k-th diagonal in s[$k].
  78. // Compute 2-norm of k-th column without under/overflow.
  79. $this->s[$k] = 0;
  80. for ($i = $k; $i < $this->m; ++$i) {
  81. $this->s[$k] = hypo($this->s[$k], $A[$i][$k]);
  82. }
  83. if ($this->s[$k] != 0.0) {
  84. if ($A[$k][$k] < 0.0) {
  85. $this->s[$k] = -$this->s[$k];
  86. }
  87. for ($i = $k; $i < $this->m; ++$i) {
  88. $A[$i][$k] /= $this->s[$k];
  89. }
  90. $A[$k][$k] += 1.0;
  91. }
  92. $this->s[$k] = -$this->s[$k];
  93. }
  94. for ($j = $k + 1; $j < $this->n; ++$j) {
  95. if (($k < $nct) & ($this->s[$k] != 0.0)) {
  96. // Apply the transformation.
  97. $t = 0;
  98. for ($i = $k; $i < $this->m; ++$i) {
  99. $t += $A[$i][$k] * $A[$i][$j];
  100. }
  101. $t = -$t / $A[$k][$k];
  102. for ($i = $k; $i < $this->m; ++$i) {
  103. $A[$i][$j] += $t * $A[$i][$k];
  104. }
  105. // Place the k-th row of A into e for the
  106. // subsequent calculation of the row transformation.
  107. $e[$j] = $A[$k][$j];
  108. }
  109. }
  110. if ($wantu and ($k < $nct)) {
  111. // Place the transformation in U for subsequent back
  112. // multiplication.
  113. for ($i = $k; $i < $this->m; ++$i) {
  114. $this->U[$i][$k] = $A[$i][$k];
  115. }
  116. }
  117. if ($k < $nrt) {
  118. // Compute the k-th row transformation and place the
  119. // k-th super-diagonal in e[$k].
  120. // Compute 2-norm without under/overflow.
  121. $e[$k] = 0;
  122. for ($i = $k + 1; $i < $this->n; ++$i) {
  123. $e[$k] = hypo($e[$k], $e[$i]);
  124. }
  125. if ($e[$k] != 0.0) {
  126. if ($e[$k + 1] < 0.0) {
  127. $e[$k] = -$e[$k];
  128. }
  129. for ($i = $k + 1; $i < $this->n; ++$i) {
  130. $e[$i] /= $e[$k];
  131. }
  132. $e[$k + 1] += 1.0;
  133. }
  134. $e[$k] = -$e[$k];
  135. if (($k + 1 < $this->m) and ($e[$k] != 0.0)) {
  136. // Apply the transformation.
  137. for ($i = $k + 1; $i < $this->m; ++$i) {
  138. $work[$i] = 0.0;
  139. }
  140. for ($j = $k + 1; $j < $this->n; ++$j) {
  141. for ($i = $k + 1; $i < $this->m; ++$i) {
  142. $work[$i] += $e[$j] * $A[$i][$j];
  143. }
  144. }
  145. for ($j = $k + 1; $j < $this->n; ++$j) {
  146. $t = -$e[$j] / $e[$k + 1];
  147. for ($i = $k + 1; $i < $this->m; ++$i) {
  148. $A[$i][$j] += $t * $work[$i];
  149. }
  150. }
  151. }
  152. if ($wantv) {
  153. // Place the transformation in V for subsequent
  154. // back multiplication.
  155. for ($i = $k + 1; $i < $this->n; ++$i) {
  156. $this->V[$i][$k] = $e[$i];
  157. }
  158. }
  159. }
  160. }
  161. // Set up the final bidiagonal matrix or order p.
  162. $p = min($this->n, $this->m + 1);
  163. if ($nct < $this->n) {
  164. $this->s[$nct] = $A[$nct][$nct];
  165. }
  166. if ($this->m < $p) {
  167. $this->s[$p - 1] = 0.0;
  168. }
  169. if ($nrt + 1 < $p) {
  170. $e[$nrt] = $A[$nrt][$p - 1];
  171. }
  172. $e[$p - 1] = 0.0;
  173. // If required, generate U.
  174. if ($wantu) {
  175. for ($j = $nct; $j < $nu; ++$j) {
  176. for ($i = 0; $i < $this->m; ++$i) {
  177. $this->U[$i][$j] = 0.0;
  178. }
  179. $this->U[$j][$j] = 1.0;
  180. }
  181. for ($k = $nct - 1; $k >= 0; --$k) {
  182. if ($this->s[$k] != 0.0) {
  183. for ($j = $k + 1; $j < $nu; ++$j) {
  184. $t = 0;
  185. for ($i = $k; $i < $this->m; ++$i) {
  186. $t += $this->U[$i][$k] * $this->U[$i][$j];
  187. }
  188. $t = -$t / $this->U[$k][$k];
  189. for ($i = $k; $i < $this->m; ++$i) {
  190. $this->U[$i][$j] += $t * $this->U[$i][$k];
  191. }
  192. }
  193. for ($i = $k; $i < $this->m; ++$i) {
  194. $this->U[$i][$k] = -$this->U[$i][$k];
  195. }
  196. $this->U[$k][$k] = 1.0 + $this->U[$k][$k];
  197. for ($i = 0; $i < $k - 1; ++$i) {
  198. $this->U[$i][$k] = 0.0;
  199. }
  200. } else {
  201. for ($i = 0; $i < $this->m; ++$i) {
  202. $this->U[$i][$k] = 0.0;
  203. }
  204. $this->U[$k][$k] = 1.0;
  205. }
  206. }
  207. }
  208. // If required, generate V.
  209. if ($wantv) {
  210. for ($k = $this->n - 1; $k >= 0; --$k) {
  211. if (($k < $nrt) and ($e[$k] != 0.0)) {
  212. for ($j = $k + 1; $j < $nu; ++$j) {
  213. $t = 0;
  214. for ($i = $k + 1; $i < $this->n; ++$i) {
  215. $t += $this->V[$i][$k] * $this->V[$i][$j];
  216. }
  217. $t = -$t / $this->V[$k + 1][$k];
  218. for ($i = $k + 1; $i < $this->n; ++$i) {
  219. $this->V[$i][$j] += $t * $this->V[$i][$k];
  220. }
  221. }
  222. }
  223. for ($i = 0; $i < $this->n; ++$i) {
  224. $this->V[$i][$k] = 0.0;
  225. }
  226. $this->V[$k][$k] = 1.0;
  227. }
  228. }
  229. // Main iteration loop for the singular values.
  230. $pp = $p - 1;
  231. $iter = 0;
  232. $eps = pow(2.0, -52.0);
  233. while ($p > 0) {
  234. // Here is where a test for too many iterations would go.
  235. // This section of the program inspects for negligible
  236. // elements in the s and e arrays. On completion the
  237. // variables kase and k are set as follows:
  238. // kase = 1 if s(p) and e[k-1] are negligible and k<p
  239. // kase = 2 if s(k) is negligible and k<p
  240. // kase = 3 if e[k-1] is negligible, k<p, and
  241. // s(k), ..., s(p) are not negligible (qr step).
  242. // kase = 4 if e(p-1) is negligible (convergence).
  243. for ($k = $p - 2; $k >= -1; --$k) {
  244. if ($k == -1) {
  245. break;
  246. }
  247. if (abs($e[$k]) <= $eps * (abs($this->s[$k]) + abs($this->s[$k + 1]))) {
  248. $e[$k] = 0.0;
  249. break;
  250. }
  251. }
  252. if ($k == $p - 2) {
  253. $kase = 4;
  254. } else {
  255. for ($ks = $p - 1; $ks >= $k; --$ks) {
  256. if ($ks == $k) {
  257. break;
  258. }
  259. $t = ($ks != $p ? abs($e[$ks]) : 0.) + ($ks != $k + 1 ? abs($e[$ks - 1]) : 0.);
  260. if (abs($this->s[$ks]) <= $eps * $t) {
  261. $this->s[$ks] = 0.0;
  262. break;
  263. }
  264. }
  265. if ($ks == $k) {
  266. $kase = 3;
  267. } elseif ($ks == $p - 1) {
  268. $kase = 1;
  269. } else {
  270. $kase = 2;
  271. $k = $ks;
  272. }
  273. }
  274. ++$k;
  275. // Perform the task indicated by kase.
  276. switch ($kase) {
  277. // Deflate negligible s(p).
  278. case 1:
  279. $f = $e[$p - 2];
  280. $e[$p - 2] = 0.0;
  281. for ($j = $p - 2; $j >= $k; --$j) {
  282. $t = hypo($this->s[$j], $f);
  283. $cs = $this->s[$j] / $t;
  284. $sn = $f / $t;
  285. $this->s[$j] = $t;
  286. if ($j != $k) {
  287. $f = -$sn * $e[$j - 1];
  288. $e[$j - 1] = $cs * $e[$j - 1];
  289. }
  290. if ($wantv) {
  291. for ($i = 0; $i < $this->n; ++$i) {
  292. $t = $cs * $this->V[$i][$j] + $sn * $this->V[$i][$p - 1];
  293. $this->V[$i][$p - 1] = -$sn * $this->V[$i][$j] + $cs * $this->V[$i][$p - 1];
  294. $this->V[$i][$j] = $t;
  295. }
  296. }
  297. }
  298. break;
  299. // Split at negligible s(k).
  300. case 2:
  301. $f = $e[$k - 1];
  302. $e[$k - 1] = 0.0;
  303. for ($j = $k; $j < $p; ++$j) {
  304. $t = hypo($this->s[$j], $f);
  305. $cs = $this->s[$j] / $t;
  306. $sn = $f / $t;
  307. $this->s[$j] = $t;
  308. $f = -$sn * $e[$j];
  309. $e[$j] = $cs * $e[$j];
  310. if ($wantu) {
  311. for ($i = 0; $i < $this->m; ++$i) {
  312. $t = $cs * $this->U[$i][$j] + $sn * $this->U[$i][$k - 1];
  313. $this->U[$i][$k - 1] = -$sn * $this->U[$i][$j] + $cs * $this->U[$i][$k - 1];
  314. $this->U[$i][$j] = $t;
  315. }
  316. }
  317. }
  318. break;
  319. // Perform one qr step.
  320. case 3:
  321. // Calculate the shift.
  322. $scale = max(max(max(max(abs($this->s[$p - 1]), abs($this->s[$p - 2])), abs($e[$p - 2])), abs($this->s[$k])), abs($e[$k]));
  323. $sp = $this->s[$p - 1] / $scale;
  324. $spm1 = $this->s[$p - 2] / $scale;
  325. $epm1 = $e[$p - 2] / $scale;
  326. $sk = $this->s[$k] / $scale;
  327. $ek = $e[$k] / $scale;
  328. $b = (($spm1 + $sp) * ($spm1 - $sp) + $epm1 * $epm1) / 2.0;
  329. $c = ($sp * $epm1) * ($sp * $epm1);
  330. $shift = 0.0;
  331. if (($b != 0.0) || ($c != 0.0)) {
  332. $shift = sqrt($b * $b + $c);
  333. if ($b < 0.0) {
  334. $shift = -$shift;
  335. }
  336. $shift = $c / ($b + $shift);
  337. }
  338. $f = ($sk + $sp) * ($sk - $sp) + $shift;
  339. $g = $sk * $ek;
  340. // Chase zeros.
  341. for ($j = $k; $j < $p - 1; ++$j) {
  342. $t = hypo($f, $g);
  343. $cs = $f / $t;
  344. $sn = $g / $t;
  345. if ($j != $k) {
  346. $e[$j - 1] = $t;
  347. }
  348. $f = $cs * $this->s[$j] + $sn * $e[$j];
  349. $e[$j] = $cs * $e[$j] - $sn * $this->s[$j];
  350. $g = $sn * $this->s[$j + 1];
  351. $this->s[$j + 1] = $cs * $this->s[$j + 1];
  352. if ($wantv) {
  353. for ($i = 0; $i < $this->n; ++$i) {
  354. $t = $cs * $this->V[$i][$j] + $sn * $this->V[$i][$j + 1];
  355. $this->V[$i][$j + 1] = -$sn * $this->V[$i][$j] + $cs * $this->V[$i][$j + 1];
  356. $this->V[$i][$j] = $t;
  357. }
  358. }
  359. $t = hypo($f, $g);
  360. $cs = $f / $t;
  361. $sn = $g / $t;
  362. $this->s[$j] = $t;
  363. $f = $cs * $e[$j] + $sn * $this->s[$j + 1];
  364. $this->s[$j + 1] = -$sn * $e[$j] + $cs * $this->s[$j + 1];
  365. $g = $sn * $e[$j + 1];
  366. $e[$j + 1] = $cs * $e[$j + 1];
  367. if ($wantu && ($j < $this->m - 1)) {
  368. for ($i = 0; $i < $this->m; ++$i) {
  369. $t = $cs * $this->U[$i][$j] + $sn * $this->U[$i][$j + 1];
  370. $this->U[$i][$j + 1] = -$sn * $this->U[$i][$j] + $cs * $this->U[$i][$j + 1];
  371. $this->U[$i][$j] = $t;
  372. }
  373. }
  374. }
  375. $e[$p - 2] = $f;
  376. $iter = $iter + 1;
  377. break;
  378. // Convergence.
  379. case 4:
  380. // Make the singular values positive.
  381. if ($this->s[$k] <= 0.0) {
  382. $this->s[$k] = ($this->s[$k] < 0.0 ? -$this->s[$k] : 0.0);
  383. if ($wantv) {
  384. for ($i = 0; $i <= $pp; ++$i) {
  385. $this->V[$i][$k] = -$this->V[$i][$k];
  386. }
  387. }
  388. }
  389. // Order the singular values.
  390. while ($k < $pp) {
  391. if ($this->s[$k] >= $this->s[$k + 1]) {
  392. break;
  393. }
  394. $t = $this->s[$k];
  395. $this->s[$k] = $this->s[$k + 1];
  396. $this->s[$k + 1] = $t;
  397. if ($wantv and ($k < $this->n - 1)) {
  398. for ($i = 0; $i < $this->n; ++$i) {
  399. $t = $this->V[$i][$k + 1];
  400. $this->V[$i][$k + 1] = $this->V[$i][$k];
  401. $this->V[$i][$k] = $t;
  402. }
  403. }
  404. if ($wantu and ($k < $this->m - 1)) {
  405. for ($i = 0; $i < $this->m; ++$i) {
  406. $t = $this->U[$i][$k + 1];
  407. $this->U[$i][$k + 1] = $this->U[$i][$k];
  408. $this->U[$i][$k] = $t;
  409. }
  410. }
  411. ++$k;
  412. }
  413. $iter = 0;
  414. --$p;
  415. break;
  416. } // end switch
  417. } // end while
  418. }
  419. /**
  420. * Return the left singular vectors.
  421. *
  422. * @return Matrix U
  423. */
  424. public function getU()
  425. {
  426. return new Matrix($this->U, $this->m, min($this->m + 1, $this->n));
  427. }
  428. /**
  429. * Return the right singular vectors.
  430. *
  431. * @return Matrix V
  432. */
  433. public function getV()
  434. {
  435. return new Matrix($this->V);
  436. }
  437. /**
  438. * Return the one-dimensional array of singular values.
  439. *
  440. * @return array diagonal of S
  441. */
  442. public function getSingularValues()
  443. {
  444. return $this->s;
  445. }
  446. /**
  447. * Return the diagonal matrix of singular values.
  448. *
  449. * @return Matrix S
  450. */
  451. public function getS()
  452. {
  453. for ($i = 0; $i < $this->n; ++$i) {
  454. for ($j = 0; $j < $this->n; ++$j) {
  455. $S[$i][$j] = 0.0;
  456. }
  457. $S[$i][$i] = $this->s[$i];
  458. }
  459. return new Matrix($S);
  460. }
  461. /**
  462. * Two norm.
  463. *
  464. * @return float max(S)
  465. */
  466. public function norm2()
  467. {
  468. return $this->s[0];
  469. }
  470. /**
  471. * Two norm condition number.
  472. *
  473. * @return float max(S)/min(S)
  474. */
  475. public function cond()
  476. {
  477. return $this->s[0] / $this->s[min($this->m, $this->n) - 1];
  478. }
  479. /**
  480. * Effective numerical matrix rank.
  481. *
  482. * @return int Number of nonnegligible singular values
  483. */
  484. public function rank()
  485. {
  486. $eps = pow(2.0, -52.0);
  487. $tol = max($this->m, $this->n) * $this->s[0] * $eps;
  488. $r = 0;
  489. $iMax = count($this->s);
  490. for ($i = 0; $i < $iMax; ++$i) {
  491. if ($this->s[$i] > $tol) {
  492. ++$r;
  493. }
  494. }
  495. return $r;
  496. }
  497. }