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

Documentation generated on Mon, 05 Jan 2009 20:38:30 +0100 by phpDocumentor 1.4.1