Source for file SingularValueDecomposition.php
Documentation is available at SingularValueDecomposition.php
* For an m-by-n matrix A with m >= n, the singular value decomposition is
* an m-by-n orthogonal matrix U, an n-by-n diagonal matrix S, and
* an n-by-n orthogonal matrix V so that A = U*S*V'.
* The singular values, sigma[$k] = S[$k][$k], are ordered so that
* sigma[0] >= sigma[1] >= ... >= sigma[n-1].
* The singular value decompostion always exists, so the constructor will
* never fail. The matrix condition number and the effective numerical
* rank can be computed from this decomposition.
* Internal storage of singular values.
* Construct the singular value decomposition
* Derived from LINPACK code.
* @param $A Rectangular matrix
* @return Structure to access U, S and V.
$A = $Arg->getArrayCopy();
$this->m = $Arg->getRowDimension();
$this->n = $Arg->getColumnDimension();
$nu = min($this->m, $this->n);
$nct = min($this->m - 1, $this->n);
$nrt = max(0, min($this->n - 2, $this->m));
// Reduce A to bidiagonal form, storing the diagonal elements
// in s and the super-diagonal elements in e.
for ($k = 0; $k < max($nct,$nrt); ++ $k) {
// Compute the transformation for the k-th column and
// place the k-th diagonal in s[$k].
// Compute 2-norm of k-th column without under/overflow.
for ($i = $k; $i < $this->m; ++ $i) {
$this->s[$k] = hypo($this->s[$k], $A[$i][$k]);
if ($this->s[$k] != 0.0) {
$this->s[$k] = - $this->s[$k];
for ($i = $k; $i < $this->m; ++ $i) {
$A[$i][$k] /= $this->s[$k];
$this->s[$k] = - $this->s[$k];
for ($j = $k + 1; $j < $this->n; ++ $j) {
if (($k < $nct) & ($this->s[$k] != 0.0)) {
// Apply the transformation.
for ($i = $k; $i < $this->m; ++ $i) {
$t += $A[$i][$k] * $A[$i][$j];
for ($i = $k; $i < $this->m; ++ $i) {
$A[$i][$j] += $t * $A[$i][$k];
// Place the k-th row of A into e for the
// subsequent calculation of the row transformation.
if ($wantu AND ($k < $nct)) {
// Place the transformation in U for subsequent back
for ($i = $k; $i < $this->m; ++ $i) {
$this->U[$i][$k] = $A[$i][$k];
// Compute the k-th row transformation and place the
// k-th super-diagonal in e[$k].
// Compute 2-norm without under/overflow.
for ($i = $k + 1; $i < $this->n; ++ $i) {
$e[$k] = hypo($e[$k], $e[$i]);
for ($i = $k + 1; $i < $this->n; ++ $i) {
if (($k+ 1 < $this->m) AND ($e[$k] != 0.0)) {
// Apply the transformation.
for ($i = $k+ 1; $i < $this->m; ++ $i) {
for ($j = $k+ 1; $j < $this->n; ++ $j) {
for ($i = $k+ 1; $i < $this->m; ++ $i) {
$work[$i] += $e[$j] * $A[$i][$j];
for ($j = $k + 1; $j < $this->n; ++ $j) {
for ($i = $k + 1; $i < $this->m; ++ $i) {
$A[$i][$j] += $t * $work[$i];
// Place the transformation in V for subsequent
for ($i = $k + 1; $i < $this->n; ++ $i) {
$this->V[$i][$k] = $e[$i];
// Set up the final bidiagonal matrix or order p.
$p = min($this->n, $this->m + 1);
$this->s[$nct] = $A[$nct][$nct];
$e[$nrt] = $A[$nrt][$p- 1];
// If required, generate U.
for ($j = $nct; $j < $nu; ++ $j) {
for ($i = 0; $i < $this->m; ++ $i) {
for ($k = $nct - 1; $k >= 0; -- $k) {
if ($this->s[$k] != 0.0) {
for ($j = $k + 1; $j < $nu; ++ $j) {
for ($i = $k; $i < $this->m; ++ $i) {
$t += $this->U[$i][$k] * $this->U[$i][$j];
$t = - $t / $this->U[$k][$k];
for ($i = $k; $i < $this->m; ++ $i) {
$this->U[$i][$j] += $t * $this->U[$i][$k];
for ($i = $k; $i < $this->m; ++ $i ) {
$this->U[$i][$k] = - $this->U[$i][$k];
$this->U[$k][$k] = 1.0 + $this->U[$k][$k];
for ($i = 0; $i < $k - 1; ++ $i) {
for ($i = 0; $i < $this->m; ++ $i) {
// If required, generate V.
for ($k = $this->n - 1; $k >= 0; -- $k) {
if (($k < $nrt) AND ($e[$k] != 0.0)) {
for ($j = $k + 1; $j < $nu; ++ $j) {
for ($i = $k + 1; $i < $this->n; ++ $i) {
$t += $this->V[$i][$k]* $this->V[$i][$j];
$t = - $t / $this->V[$k+ 1][$k];
for ($i = $k + 1; $i < $this->n; ++ $i) {
$this->V[$i][$j] += $t * $this->V[$i][$k];
for ($i = 0; $i < $this->n; ++ $i) {
// Main iteration loop for the singular values.
// Here is where a test for too many iterations would go.
// This section of the program inspects for negligible
// elements in the s and e arrays. On completion the
// variables kase and k are set as follows:
// kase = 1 if s(p) and e[k-1] are negligible and k<p
// kase = 2 if s(k) is negligible and k<p
// kase = 3 if e[k-1] is negligible, k<p, and
// s(k), ..., s(p) are not negligible (qr step).
// kase = 4 if e(p-1) is negligible (convergence).
for ($k = $p - 2; $k >= - 1; -- $k) {
if (abs($e[$k]) <= $eps * (abs($this->s[$k]) + abs($this->s[$k+ 1]))) {
for ($ks = $p - 1; $ks >= $k; -- $ks) {
$t = ($ks != $p ? abs($e[$ks]) : 0.) + ($ks != $k + 1 ? abs($e[$ks- 1]) : 0.);
if (abs($this->s[$ks]) <= $eps * $t) {
} else if ($ks == $p- 1) {
// Perform the task indicated by kase.
// Deflate negligible s(p).
for ($j = $p - 2; $j >= $k; -- $j) {
$t = hypo($this->s[$j],$f);
$e[$j- 1] = $cs * $e[$j- 1];
for ($i = 0; $i < $this->n; ++ $i) {
$t = $cs * $this->V[$i][$j] + $sn * $this->V[$i][$p- 1];
$this->V[$i][$p- 1] = - $sn * $this->V[$i][$j] + $cs * $this->V[$i][$p- 1];
// Split at negligible s(k).
for ($j = $k; $j < $p; ++ $j) {
$t = hypo($this->s[$j], $f);
for ($i = 0; $i < $this->m; ++ $i) {
$t = $cs * $this->U[$i][$j] + $sn * $this->U[$i][$k- 1];
$this->U[$i][$k- 1] = - $sn * $this->U[$i][$j] + $cs * $this->U[$i][$k- 1];
abs($this->s[$p- 1]),abs($this->s[$p- 2])),abs($e[$p- 2])),
abs($this->s[$k])), abs($e[$k]));
$sp = $this->s[$p- 1] / $scale;
$spm1 = $this->s[$p- 2] / $scale;
$epm1 = $e[$p- 2] / $scale;
$sk = $this->s[$k] / $scale;
$b = (($spm1 + $sp) * ($spm1 - $sp) + $epm1 * $epm1) / 2.0;
$c = ($sp * $epm1) * ($sp * $epm1);
if (($b != 0.0) || ($c != 0.0)) {
$shift = sqrt($b * $b + $c);
$shift = $c / ($b + $shift);
$f = ($sk + $sp) * ($sk - $sp) + $shift;
for ($j = $k; $j < $p- 1; ++ $j) {
$f = $cs * $this->s[$j] + $sn * $e[$j];
$e[$j] = $cs * $e[$j] - $sn * $this->s[$j];
$g = $sn * $this->s[$j+ 1];
$this->s[$j+ 1] = $cs * $this->s[$j+ 1];
for ($i = 0; $i < $this->n; ++ $i) {
$t = $cs * $this->V[$i][$j] + $sn * $this->V[$i][$j+ 1];
$this->V[$i][$j+ 1] = - $sn * $this->V[$i][$j] + $cs * $this->V[$i][$j+ 1];
$f = $cs * $e[$j] + $sn * $this->s[$j+ 1];
$this->s[$j+ 1] = - $sn * $e[$j] + $cs * $this->s[$j+ 1];
$e[$j+ 1] = $cs * $e[$j+ 1];
if ($wantu && ($j < $this->m - 1)) {
for ($i = 0; $i < $this->m; ++ $i) {
$t = $cs * $this->U[$i][$j] + $sn * $this->U[$i][$j+ 1];
$this->U[$i][$j+ 1] = - $sn * $this->U[$i][$j] + $cs * $this->U[$i][$j+ 1];
// Make the singular values positive.
if ($this->s[$k] <= 0.0) {
$this->s[$k] = ($this->s[$k] < 0.0 ? - $this->s[$k] : 0.0);
for ($i = 0; $i <= $pp; ++ $i) {
$this->V[$i][$k] = - $this->V[$i][$k];
// Order the singular values.
if ($this->s[$k] >= $this->s[$k+ 1]) {
$this->s[$k] = $this->s[$k+ 1];
if ($wantv AND ($k < $this->n - 1)) {
for ($i = 0; $i < $this->n; ++ $i) {
$this->V[$i][$k+ 1] = $this->V[$i][$k];
if ($wantu AND ($k < $this->m- 1)) {
for ($i = 0; $i < $this->m; ++ $i) {
$this->U[$i][$k+ 1] = $this->U[$i][$k];
* Return the left singular vectors
return new Matrix($this->U, $this->m, min($this->m + 1, $this->n));
* Return the right singular vectors
return new Matrix($this->V);
* Return the one-dimensional array of singular values
* Return the diagonal matrix of singular values
for ($i = 0; $i < $this->n; ++ $i) {
for ($j = 0; $j < $this->n; ++ $j) {
$S[$i][$i] = $this->s[$i];
public function norm2() {
* Two norm condition number
return $this->s[0] / $this->s[min($this->m, $this->n) - 1];
* Effective numerical matrix rank
* @return Number of nonnegligible singular values.
$tol = max($this->m, $this->n) * $this->s[0] * $eps;
for ($i = 0; $i < count($this->s); ++ $i) {
if ($this->s[$i] > $tol) {
} // class SingularValueDecomposition
|