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

Source for file QRDecomposition.php

Documentation is available at QRDecomposition.php

  1. <?php
  2. /**  
  3. * @package JAMA
  4. *
  5. *  For an m-by-n matrix A with m >= n, the QR decomposition is an m-by-n
  6. *  orthogonal matrix Q and an n-by-n upper triangular matrix R so that
  7. *  A = Q*R.
  8. *   
  9. *  The QR decompostion always exists, even if the matrix does not have
  10. *  full rank, so the constructor will never fail.  The primary use of the
  11. *  QR decomposition is in the least squares solution of nonsquare systems
  12. *  of simultaneous linear equations.  This will fail if isFullRank()
  13. *  returns false.
  14. *  
  15. * @author  Paul Meagher
  16. * @license PHP v3.0
  17. * @version 1.1
  18. */
  19. class QRDecomposition {
  20.   /**
  21.   * Array for internal storage of decomposition.
  22.   * @var array 
  23.   */     
  24.   var $QR = array();
  25.  
  26.   /**
  27.   * Row dimension.
  28.   * @var integer 
  29.   */    
  30.   var $m;
  31.  
  32.   /**
  33.   * Column dimension.
  34.   * @var integer 
  35.   */
  36.   var $n;
  37.  
  38.   /**
  39.   * Array for internal storage of diagonal of R.
  40.   * @var  array 
  41.   */
  42.   var $Rdiag = array();
  43.  
  44.   /**
  45.   * QR Decomposition computed by Householder reflections.
  46.   * @param matrix $A Rectangular matrix
  47.   * @return Structure to access R and the Householder vectors and compute Q.
  48.   */
  49.   function QRDecomposition($A) {
  50.     if( is_a($A, 'Matrix') ) {
  51.       // Initialize.
  52.       $this->QR = $A->getArrayCopy();
  53.       $this->m  = $A->getRowDimension();
  54.       $this->n  = $A->getColumnDimension();
  55.       // Main loop.
  56.       for ($k = 0; $k < $this->n; $k++) {
  57.         // Compute 2-norm of k-th column without under/overflow.
  58.         $nrm = 0.0;
  59.         for ($i = $k; $i < $this->m; $i++)
  60.           $nrm = hypo($nrm, $this->QR[$i][$k]);
  61.         if ($nrm != 0.0) {
  62.           // Form k-th Householder vector.
  63.           if ($this->QR[$k][$k] < 0)
  64.             $nrm = -$nrm;
  65.           for ($i = $k; $i < $this->m; $i++)
  66.             $this->QR[$i][$k] /= $nrm;
  67.           $this->QR[$k][$k] += 1.0;
  68.           // Apply transformation to remaining columns.
  69.           for ($j = $k+1; $j < $this->n; $j++) {
  70.             $s = 0.0;
  71.             for ($i = $k; $i < $this->m; $i++)
  72.               $s += $this->QR[$i][$k] * $this->QR[$i][$j];
  73.             $s = -$s/$this->QR[$k][$k];
  74.             for ($i = $k; $i < $this->m; $i++)
  75.               $this->QR[$i][$j] += $s * $this->QR[$i][$k];
  76.           }
  77.         }
  78.             $this->Rdiag[$k] = -$nrm;
  79.       }    
  80.       } else 
  81.           trigger_error(ArgumentTypeException, ERROR);
  82.   }
  83.  
  84.   /**
  85.   * Is the matrix full rank?
  86.   * @return boolean true if R, and hence A, has full rank, else false.
  87.   */
  88.   function isFullRank() {
  89.     for ($j = 0; $j < $this->n; $j++)
  90.       if ($this->Rdiag[$j] == 0)
  91.         return false;
  92.     return true;
  93.   }
  94.  
  95.   /**
  96.   * Return the Householder vectors
  97.   * @return Matrix Lower trapezoidal matrix whose columns define the reflections
  98.   */
  99.   function getH() {
  100.     for ($i = 0; $i < $this->m; $i++) {
  101.       for ($j = 0; $j < $this->n; $j++) {
  102.         if ($i >= $j)
  103.           $H[$i][$j] = $this->QR[$i][$j];
  104.         else
  105.           $H[$i][$j] = 0.0;
  106.       }
  107.     }
  108.     return new Matrix($H);
  109.   }
  110.  
  111.   /**
  112.   * Return the upper triangular factor
  113.   * @return Matrix upper triangular factor
  114.   */
  115.   function getR() {
  116.     for ($i = 0; $i < $this->n; $i++) {
  117.       for ($j = 0; $j < $this->n; $j++) {
  118.         if ($i < $j)
  119.           $R[$i][$j] = $this->QR[$i][$j];
  120.         else if ($i == $j)
  121.           $R[$i][$j] = $this->Rdiag[$i];
  122.         else
  123.           $R[$i][$j] = 0.0;
  124.       }
  125.     }
  126.     return new Matrix($R);
  127.   }
  128.  
  129.   /**
  130.   * Generate and return the (economy-sized) orthogonal factor
  131.   * @return Matrix orthogonal factor
  132.   */
  133.   function getQ() {
  134.     for ($k = $this->n-1; $k >= 0; $k--) {
  135.       for ($i = 0; $i < $this->m; $i++)
  136.         $Q[$i][$k] = 0.0;
  137.       $Q[$k][$k] = 1.0;
  138.       for ($j = $k; $j < $this->n; $j++) {
  139.         if ($this->QR[$k][$k] != 0) {
  140.           $s = 0.0;
  141.           for ($i = $k; $i < $this->m; $i++)
  142.             $s += $this->QR[$i][$k] * $Q[$i][$j];
  143.           $s = -$s/$this->QR[$k][$k];
  144.           for ($i = $k; $i < $this->m; $i++)
  145.             $Q[$i][$j] += $s * $this->QR[$i][$k];
  146.         }
  147.       }
  148.     }   
  149.     /* 
  150.       for( $i = 0; $i < count($Q); $i++ ) 
  151.           for( $j = 0; $j < count($Q); $j++ ) 
  152.               if(! isset($Q[$i][$j]) )
  153.                   $Q[$i][$j] = 0;
  154.       */
  155.       return new Matrix($Q);
  156.   }
  157.                     
  158.   /**
  159.   * Least squares solution of A*X = B
  160.   * @param Matrix $B A Matrix with as many rows as A and any number of columns.
  161.   * @return Matrix Matrix that minimizes the two norm of Q*R*X-B.
  162.   */
  163.   function solve($B) {
  164.     if ($B->getRowDimension() == $this->m) {
  165.       if ($this->isFullRank()) {
  166.         // Copy right hand side
  167.         $nx = $B->getColumnDimension();                   
  168.         $X  = $B->getArrayCopy();
  169.         // Compute Y = transpose(Q)*B
  170.         for ($k = 0; $k < $this->n; $k++) {
  171.           for ($j = 0; $j < $nx; $j++) {
  172.             $s = 0.0;
  173.             for ($i = $k; $i < $this->m; $i++)
  174.               $s += $this->QR[$i][$k] * $X[$i][$j];
  175.             $s = -$s/$this->QR[$k][$k];
  176.             for ($i = $k; $i < $this->m; $i++)
  177.               $X[$i][$j] += $s * $this->QR[$i][$k];
  178.           }
  179.         }    
  180.         // Solve R*X = Y;
  181.         for ($k = $this->n-1; $k >= 0; $k--) {
  182.           for ($j = 0; $j < $nx; $j++)
  183.             $X[$k][$j] /= $this->Rdiag[$k];
  184.           for ($i = 0; $i < $k; $i++)
  185.             for ($j = 0; $j < $nx; $j++)
  186.               $X[$i][$j] -= $X[$k][$j]* $this->QR[$i][$k];
  187.         }
  188.         $X = new Matrix($X);
  189.         return ($X->getMatrix(0, $this->n-1, 0, $nx));
  190.       } else 
  191.     } else 
  192.   }
  193. }

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