5
* For an m-by-n matrix A with m >= n, the singular value decomposition is
6
* an m-by-n orthogonal matrix U, an n-by-n diagonal matrix S, and
7
* an n-by-n orthogonal matrix V so that A = U*S*V'.
9
* The singular values, sigma[$k] = S[$k][$k], are ordered so that
10
* sigma[0] >= sigma[1] >= ... >= sigma[n-1].
12
* The singular value decompostion always exists, so the constructor will
13
* never fail. The matrix condition number and the effective numerical
14
* rank can be computed from this decomposition.
16
* @author Paul Meagher
20
class SingularValueDecomposition {
23
* Internal storage of U.
29
* Internal storage of V.
35
* Internal storage of singular values.
53
* Construct the singular value decomposition
55
* Derived from LINPACK code.
57
* @param $A Rectangular matrix
58
* @return Structure to access U, S and V.
60
function SingularValueDecomposition ($Arg) {
64
$A = $Arg->getArrayCopy();
65
$this->m = $Arg->getRowDimension();
66
$this->n = $Arg->getColumnDimension();
67
$nu = min($this->m, $this->n);
72
$nct = min($this->m - 1, $this->n);
73
$nrt = max(0, min($this->n - 2, $this->m));
75
// Reduce A to bidiagonal form, storing the diagonal elements
76
// in s and the super-diagonal elements in e.
78
for ($k = 0; $k < max($nct,$nrt); $k++) {
81
// Compute the transformation for the k-th column and
82
// place the k-th diagonal in s[$k].
83
// Compute 2-norm of k-th column without under/overflow.
85
for ($i = $k; $i < $this->m; $i++)
86
$this->s[$k] = hypo($this->s[$k], $A[$i][$k]);
87
if ($this->s[$k] != 0.0) {
89
$this->s[$k] = -$this->s[$k];
90
for ($i = $k; $i < $this->m; $i++)
91
$A[$i][$k] /= $this->s[$k];
94
$this->s[$k] = -$this->s[$k];
97
for ($j = $k + 1; $j < $this->n; $j++) {
98
if (($k < $nct) & ($this->s[$k] != 0.0)) {
99
// Apply the transformation.
101
for ($i = $k; $i < $this->m; $i++)
102
$t += $A[$i][$k] * $A[$i][$j];
103
$t = -$t / $A[$k][$k];
104
for ($i = $k; $i < $this->m; $i++)
105
$A[$i][$j] += $t * $A[$i][$k];
106
// Place the k-th row of A into e for the
107
// subsequent calculation of the row transformation.
112
if ($wantu AND ($k < $nct)) {
113
// Place the transformation in U for subsequent back
115
for ($i = $k; $i < $this->m; $i++)
116
$this->U[$i][$k] = $A[$i][$k];
120
// Compute the k-th row transformation and place the
121
// k-th super-diagonal in e[$k].
122
// Compute 2-norm without under/overflow.
124
for ($i = $k + 1; $i < $this->n; $i++)
125
$e[$k] = hypo($e[$k], $e[$i]);
129
for ($i = $k + 1; $i < $this->n; $i++)
134
if (($k+1 < $this->m) AND ($e[$k] != 0.0)) {
135
// Apply the transformation.
136
for ($i = $k+1; $i < $this->m; $i++)
138
for ($j = $k+1; $j < $this->n; $j++)
139
for ($i = $k+1; $i < $this->m; $i++)
140
$work[$i] += $e[$j] * $A[$i][$j];
141
for ($j = $k + 1; $j < $this->n; $j++) {
142
$t = -$e[$j] / $e[$k+1];
143
for ($i = $k + 1; $i < $this->m; $i++)
144
$A[$i][$j] += $t * $work[$i];
148
// Place the transformation in V for subsequent
149
// back multiplication.
150
for ($i = $k + 1; $i < $this->n; $i++)
151
$this->V[$i][$k] = $e[$i];
156
// Set up the final bidiagonal matrix or order p.
157
$p = min($this->n, $this->m + 1);
159
$this->s[$nct] = $A[$nct][$nct];
161
$this->s[$p-1] = 0.0;
163
$e[$nrt] = $A[$nrt][$p-1];
165
// If required, generate U.
167
for ($j = $nct; $j < $nu; $j++) {
168
for ($i = 0; $i < $this->m; $i++)
169
$this->U[$i][$j] = 0.0;
170
$this->U[$j][$j] = 1.0;
172
for ($k = $nct - 1; $k >= 0; $k--) {
173
if ($this->s[$k] != 0.0) {
174
for ($j = $k + 1; $j < $nu; $j++) {
176
for ($i = $k; $i < $this->m; $i++)
177
$t += $this->U[$i][$k] * $this->U[$i][$j];
178
$t = -$t / $this->U[$k][$k];
179
for ($i = $k; $i < $this->m; $i++)
180
$this->U[$i][$j] += $t * $this->U[$i][$k];
182
for ($i = $k; $i < $this->m; $i++ )
183
$this->U[$i][$k] = -$this->U[$i][$k];
184
$this->U[$k][$k] = 1.0 + $this->U[$k][$k];
185
for ($i = 0; $i < $k - 1; $i++)
186
$this->U[$i][$k] = 0.0;
188
for ($i = 0; $i < $this->m; $i++)
189
$this->U[$i][$k] = 0.0;
190
$this->U[$k][$k] = 1.0;
195
// If required, generate V.
197
for ($k = $this->n - 1; $k >= 0; $k--) {
198
if (($k < $nrt) AND ($e[$k] != 0.0)) {
199
for ($j = $k + 1; $j < $nu; $j++) {
201
for ($i = $k + 1; $i < $this->n; $i++)
202
$t += $this->V[$i][$k]* $this->V[$i][$j];
203
$t = -$t / $this->V[$k+1][$k];
204
for ($i = $k + 1; $i < $this->n; $i++)
205
$this->V[$i][$j] += $t * $this->V[$i][$k];
208
for ($i = 0; $i < $this->n; $i++)
209
$this->V[$i][$k] = 0.0;
210
$this->V[$k][$k] = 1.0;
214
// Main iteration loop for the singular values.
217
$eps = pow(2.0, -52.0);
220
// Here is where a test for too many iterations would go.
221
// This section of the program inspects for negligible
222
// elements in the s and e arrays. On completion the
223
// variables kase and k are set as follows:
224
// kase = 1 if s(p) and e[k-1] are negligible and k<p
225
// kase = 2 if s(k) is negligible and k<p
226
// kase = 3 if e[k-1] is negligible, k<p, and
227
// s(k), ..., s(p) are not negligible (qr step).
228
// kase = 4 if e(p-1) is negligible (convergence).
230
for ($k = $p - 2; $k >= -1; $k--) {
233
if (abs($e[$k]) <= $eps * (abs($this->s[$k]) + abs($this->s[$k+1]))) {
241
for ($ks = $p - 1; $ks >= $k; $ks--) {
244
$t = ($ks != $p ? abs($e[$ks]) : 0.) + ($ks != $k + 1 ? abs($e[$ks-1]) : 0.);
245
if (abs($this->s[$ks]) <= $eps * $t) {
252
else if ($ks == $p-1)
261
// Perform the task indicated by kase.
263
// Deflate negligible s(p).
267
for ($j = $p - 2; $j >= $k; $j--) {
268
$t = hypo($this->s[$j],$f);
269
$cs = $this->s[$j] / $t;
273
$f = -$sn * $e[$j-1];
274
$e[$j-1] = $cs * $e[$j-1];
277
for ($i = 0; $i < $this->n; $i++) {
278
$t = $cs * $this->V[$i][$j] + $sn * $this->V[$i][$p-1];
279
$this->V[$i][$p-1] = -$sn * $this->V[$i][$j] + $cs * $this->V[$i][$p-1];
280
$this->V[$i][$j] = $t;
285
// Split at negligible s(k).
289
for ($j = $k; $j < $p; $j++) {
290
$t = hypo($this->s[$j], $f);
291
$cs = $this->s[$j] / $t;
295
$e[$j] = $cs * $e[$j];
297
for ($i = 0; $i < $this->m; $i++) {
298
$t = $cs * $this->U[$i][$j] + $sn * $this->U[$i][$k-1];
299
$this->U[$i][$k-1] = -$sn * $this->U[$i][$j] + $cs * $this->U[$i][$k-1];
300
$this->U[$i][$j] = $t;
305
// Perform one qr step.
307
// Calculate the shift.
308
$scale = max(max(max(max(
309
abs($this->s[$p-1]),abs($this->s[$p-2])),abs($e[$p-2])),
310
abs($this->s[$k])), abs($e[$k]));
311
$sp = $this->s[$p-1] / $scale;
312
$spm1 = $this->s[$p-2] / $scale;
313
$epm1 = $e[$p-2] / $scale;
314
$sk = $this->s[$k] / $scale;
315
$ek = $e[$k] / $scale;
316
$b = (($spm1 + $sp) * ($spm1 - $sp) + $epm1 * $epm1) / 2.0;
317
$c = ($sp * $epm1) * ($sp * $epm1);
319
if (($b != 0.0) || ($c != 0.0)) {
320
$shift = sqrt($b * $b + $c);
323
$shift = $c / ($b + $shift);
325
$f = ($sk + $sp) * ($sk - $sp) + $shift;
328
for ($j = $k; $j < $p-1; $j++) {
334
$f = $cs * $this->s[$j] + $sn * $e[$j];
335
$e[$j] = $cs * $e[$j] - $sn * $this->s[$j];
336
$g = $sn * $this->s[$j+1];
337
$this->s[$j+1] = $cs * $this->s[$j+1];
339
for ($i = 0; $i < $this->n; $i++) {
340
$t = $cs * $this->V[$i][$j] + $sn * $this->V[$i][$j+1];
341
$this->V[$i][$j+1] = -$sn * $this->V[$i][$j] + $cs * $this->V[$i][$j+1];
342
$this->V[$i][$j] = $t;
349
$f = $cs * $e[$j] + $sn * $this->s[$j+1];
350
$this->s[$j+1] = -$sn * $e[$j] + $cs * $this->s[$j+1];
352
$e[$j+1] = $cs * $e[$j+1];
353
if ($wantu && ($j < $this->m - 1)) {
354
for ($i = 0; $i < $this->m; $i++) {
355
$t = $cs * $this->U[$i][$j] + $sn * $this->U[$i][$j+1];
356
$this->U[$i][$j+1] = -$sn * $this->U[$i][$j] + $cs * $this->U[$i][$j+1];
357
$this->U[$i][$j] = $t;
366
// Make the singular values positive.
367
if ($this->s[$k] <= 0.0) {
368
$this->s[$k] = ($this->s[$k] < 0.0 ? -$this->s[$k] : 0.0);
370
for ($i = 0; $i <= $pp; $i++)
371
$this->V[$i][$k] = -$this->V[$i][$k];
374
// Order the singular values.
376
if ($this->s[$k] >= $this->s[$k+1])
379
$this->s[$k] = $this->s[$k+1];
381
if ($wantv AND ($k < $this->n - 1)) {
382
for ($i = 0; $i < $this->n; $i++) {
383
$t = $this->V[$i][$k+1];
384
$this->V[$i][$k+1] = $this->V[$i][$k];
385
$this->V[$i][$k] = $t;
388
if ($wantu AND ($k < $this->m-1)) {
389
for ($i = 0; $i < $this->m; $i++) {
390
$t = $this->U[$i][$k+1];
391
$this->U[$i][$k+1] = $this->U[$i][$k];
392
$this->U[$i][$k] = $t;
404
echo "<p>Output A</p>";
408
echo "<p>Matrix U</p>";
413
echo "<p>Matrix V</p>";
418
echo "<p>Vector S</p>";
428
* Return the left singular vectors
433
return new Matrix($this->U, $this->m, min($this->m + 1, $this->n));
437
* Return the right singular vectors
442
return new Matrix($this->V);
446
* Return the one-dimensional array of singular values
448
* @return diagonal of S.
450
function getSingularValues() {
455
* Return the diagonal matrix of singular values
460
for ($i = 0; $i < $this->n; $i++) {
461
for ($j = 0; $j < $this->n; $j++)
463
$S[$i][$i] = $this->s[$i];
465
return new Matrix($S);
478
* Two norm condition number
480
* @return max(S)/min(S)
483
return $this->s[0] / $this->s[min($this->m, $this->n) - 1];
487
* Effective numerical matrix rank
489
* @return Number of nonnegligible singular values.
492
$eps = pow(2.0, -52.0);
493
$tol = max($this->m, $this->n) * $this->s[0] * $eps;
495
for ($i = 0; $i < count($this->s); $i++) {
496
if ($this->s[$i] > $tol)