JAMA
[ class tree: JAMA ] [ index: JAMA ] [ all elements ]

Source for file SingularValueDecomposition.php

Documentation is available at SingularValueDecomposition.php

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

Documentation generated on Fri, 12 Oct 2012 00:17:45 +0200 by phpDocumentor 1.4.4