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(0min($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[$k0;
  84.         for ($i $k$i $this->m$i++)
  85.           $this->s[$khypo($this->s[$k]$A[$i][$k]);
  86.         if ($this->s[$k!= 0.0{
  87.           if ($A[$k][$k0.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[$k0;
  123.         for ($i $k 1$i $this->n$i++)
  124.           $e[$khypo($e[$k]$e[$i]);
  125.         if ($e[$k!= 0.0{
  126.           if ($e[$k+10.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+$this->mAND ($e[$k!= 0.0)) {
  134.           // Apply the transformation.
  135.           for ($i $k+1$i $this->m$i++)
  136.             $work[$i0.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-10.0;
  161.     if ($nrt $p)
  162.       $e[$nrt$A[$nrt][$p-1];
  163.     $e[$p-10.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][$j0.0;
  169.         $this->U[$j][$j1.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][$k1.0 $this->U[$k][$k];
  184.           for ($i 0$i $k 1$i++)
  185.              $this->U[$i][$k0.0;
  186.          else {
  187.            for ($i 0$i $this->m$i++)
  188.              $this->U[$i][$k0.0;
  189.            $this->U[$k][$k1.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 $nrtAND ($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][$k0.0;
  209.         $this->V[$k][$k1.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[$k0.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 abs($e[$ks-1]0.);
  244.           if (abs($this->s[$ks]<= $eps $t)  {
  245.             $this->s[$ks0.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-20.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-10.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 $epm12.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[$k0.0 ? -$this->s[$k0.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 
  430.   */
  431.   function getU({
  432.     return new Matrix($this->U$this->mmin($this->m + 1$this->n));
  433.   }
  434.  
  435.   /**
  436.   * Return the right singular vectors
  437.   * @access public
  438.   * @return 
  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 
  457.   */
  458.   function getS({
  459.     for ($i 0$i $this->n$i++{
  460.       for ($j 0$j $this->n$j++)
  461.         $S[$i][$j0.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->n1];
  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