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

Source for file EigenvalueDecomposition.php

Documentation is available at EigenvalueDecomposition.php

  1. <?php
  2. /**
  3. * @package JAMA
  4. *
  5. *  Class to obtain eigenvalues and eigenvectors of a real matrix.
  6. *
  7. *  If A is symmetric, then A = V*D*V' where the eigenvalue matrix D
  8. *  is diagonal and the eigenvector matrix V is orthogonal (i.e.
  9. *  A = V.times(D.times(V.transpose())) and V.times(V.transpose())
  10. *  equals the identity matrix).
  11. *
  12. *  If A is not symmetric, then the eigenvalue matrix D is block diagonal
  13. *  with the real eigenvalues in 1-by-1 blocks and any complex eigenvalues,
  14. *  lambda + i*mu, in 2-by-2 blocks, [lambda, mu; -mu, lambda].  The
  15. *  columns of V represent the eigenvectors in the sense that A*V = V*D,
  16. *  i.e. A.times(V) equals V.times(D).  The matrix V may be badly
  17. *  conditioned, or even singular, so the validity of the equation
  18. *  A = V*D*inverse(V) depends upon V.cond().
  19. *
  20. * @author  Paul Meagher
  21. * @license PHP v3.0
  22. * @version 1.1
  23. */
  24.  
  25.   /**
  26.   * Row and column dimension (square matrix).
  27.   * @var int 
  28.   */
  29.   var $n;
  30.  
  31.   /**
  32.   * Internal symmetry flag.
  33.  
  34.   * @var int 
  35.   */
  36.   var $issymmetric;
  37.  
  38.   /**
  39.   * Arrays for internal storage of eigenvalues.
  40.   * @var array 
  41.   */
  42.   var $d = array();
  43.   var $e = array();
  44.  
  45.   /**
  46.   * Array for internal storage of eigenvectors.
  47.   * @var array 
  48.   */
  49.   var $V = array();
  50.  
  51.   /**
  52.   * Array for internal storage of nonsymmetric Hessenberg form.
  53.   * @var array 
  54.   */
  55.   var $H = array();
  56.  
  57.   /**
  58.   * Working storage for nonsymmetric algorithm.
  59.   * @var array 
  60.   */
  61.   var $ort;
  62.  
  63.   /**
  64.   * Used for complex scalar division.
  65.   * @var float 
  66.   */
  67.   var $cdivr;
  68.   var $cdivi;
  69.  
  70.   /**
  71.   * Symmetric Householder reduction to tridiagonal form.
  72.   * @access private
  73.   */
  74.   function tred2 () {
  75.     //  This is derived from the Algol procedures tred2 by
  76.     //  Bowdler, Martin, Reinsch, and Wilkinson, Handbook for
  77.     //  Auto. Comp., Vol.ii-Linear Algebra, and the corresponding
  78.     //  Fortran subroutine in EISPACK.
  79.     $this->d = $this->V[$this->n-1];
  80.     // Householder reduction to tridiagonal form.   
  81.     for ($i = $this->n-1; $i > 0; $i--) { 
  82.     $i_ = $i -1;
  83.       // Scale to avoid under/overflow.  
  84.       $h = $scale = 0.0;
  85.       $scale += array_sum(array_map(abs, $this->d));
  86.       if ($scale == 0.0) {
  87.         $this->e[$i] = $this->d[$i_];
  88.         $this->d = array_slice($this->V[$i_], 0, $i_);
  89.     for ($j = 0; $j < $i; $j++) {
  90.           $this->V[$j][$i] = $this->V[$i][$j] = 0.0;
  91.         }
  92.       } else {
  93.         // Generate Householder vector.
  94.         for ($k = 0; $k < $i; $k++) {
  95.           $this->d[$k] /= $scale;
  96.           $h += pow($this->d[$k], 2);
  97.         }
  98.         $f = $this->d[$i_];
  99.         $g = sqrt($h);
  100.         if ($f > 0)
  101.           $g = -$g;
  102.         $this->e[$i] = $scale * $g;
  103.         $h = $h - $f * $g;
  104.         $this->d[$i_] = $f - $g;
  105.         for ($j = 0; $j < $i; $j++)
  106.           $this->e[$j] = 0.0;
  107.          // Apply similarity transformation to remaining columns.
  108.          for ($j = 0; $j < $i; $j++) {
  109.            $f = $this->d[$j];
  110.            $this->V[$j][$i] = $f;
  111.            $g = $this->e[$j] + $this->V[$j][$j] * $f;
  112.            for ($k = $j+1; $k <= $i_; $k++) {
  113.              $g += $this->V[$k][$j] * $this->d[$k];
  114.              $this->e[$k] += $this->V[$k][$j] * $f;
  115.            }
  116.            $this->e[$j] = $g;
  117.          }
  118.          $f = 0.0;
  119.          for ($j = 0; $j < $i; $j++) {
  120.            $this->e[$j] /= $h;
  121.            $f += $this->e[$j] * $this->d[$j];
  122.          }
  123.          $hh = $f / (2 * $h);
  124.          for ($j=0; $j < $i; $j++)
  125.            $this->e[$j] -= $hh * $this->d[$j];
  126.          for ($j = 0; $j < $i; $j++) {
  127.            $f = $this->d[$j];
  128.            $g = $this->e[$j];
  129.            for ($k = $j; $k <= $i_; $k++)
  130.              $this->V[$k][$j] -= ($f * $this->e[$k] + $g * $this->d[$k]);
  131.            $this->d[$j] = $this->V[$i-1][$j];
  132.            $this->V[$i][$j] = 0.0;
  133.          }
  134.       }
  135.       $this->d[$i] = $h;
  136.     }   
  137.     // Accumulate transformations.
  138.     for ($i = 0; $i < $this->n-1; $i++) {
  139.       $this->V[$this->n-1][$i] = $this->V[$i][$i];
  140.       $this->V[$i][$i] = 1.0;
  141.       $h = $this->d[$i+1];
  142.       if ($h != 0.0) {
  143.         for ($k = 0; $k <= $i; $k++)
  144.           $this->d[$k] = $this->V[$k][$i+1] / $h;
  145.         for ($j = 0; $j <= $i; $j++) {
  146.           $g = 0.0;
  147.           for ($k = 0; $k <= $i; $k++)
  148.             $g += $this->V[$k][$i+1] * $this->V[$k][$j];
  149.           for ($k = 0; $k <= $i; $k++)
  150.             $this->V[$k][$j] -= $g * $this->d[$k];
  151.         }
  152.       }
  153.       for ($k = 0; $k <= $i; $k++)
  154.         $this->V[$k][$i+1] = 0.0;
  155.     } 
  156.  
  157.   $this->d = $this->V[$this->n-1];
  158.   $this->V[$this->n-1] = array_fill(0, $j, 0.0);    
  159.     $this->V[$this->n-1][$this->n-1] = 1.0;
  160.     $this->e[0] = 0.0;
  161.   }
  162.  
  163.   /**
  164.   * Symmetric tridiagonal QL algorithm.
  165.   *
  166.   * This is derived from the Algol procedures tql2, by
  167.   * Bowdler, Martin, Reinsch, and Wilkinson, Handbook for
  168.   * Auto. Comp., Vol.ii-Linear Algebra, and the corresponding
  169.   * Fortran subroutine in EISPACK.
  170.   *
  171.   * @access private
  172.   */
  173.   function tql2 () {
  174.     for ($i = 1; $i < $this->n; $i++)
  175.       $this->e[$i-1] = $this->e[$i];
  176.     $this->e[$this->n-1] = 0.0;   
  177.     $f = 0.0;
  178.     $tst1 = 0.0;
  179.     $eps  = pow(2.0,-52.0);
  180.     for ($l = 0; $l < $this->n; $l++) {
  181.       // Find small subdiagonal element
  182.       $tst1 = max($tst1, abs($this->d[$l]) + abs($this->e[$l]));
  183.       $m = $l;
  184.       while ($m < $this->n) {
  185.         if (abs($this->e[$m]) <= $eps * $tst1)
  186.           break;
  187.         $m++;
  188.       }   
  189.       // If m == l, $this->d[l] is an eigenvalue,
  190.       // otherwise, iterate.  
  191.       if ($m > $l) {
  192.         $iter = 0;
  193.         do {
  194.           // Could check iteration count here.          
  195.           $iter += 1;  
  196.           // Compute implicit shift
  197.           $g = $this->d[$l];
  198.           $p = ($this->d[$l+1] - $g) / (2.0 * $this->e[$l]);
  199.           $r = hypo($p, 1.0);
  200.           if ($p < 0)
  201.             $r *= -1;
  202.       $this->d[$l] = $this->e[$l] / ($p + $r);
  203.           $this->d[$l+1] = $this->e[$l] * ($p + $r);
  204.           $dl1 = $this->d[$l+1];
  205.           $h = $g - $this->d[$l];
  206.           for ($i = $l + 2; $i < $this->n; $i++)
  207.             $this->d[$i] -= $h;
  208.           $f += $h;
  209.           // Implicit QL transformation.
  210.           $p = $this->d[$m];
  211.           $c = 1.0;
  212.           $c2 = $c3 = $c;
  213.           $el1 = $this->e[$l + 1];
  214.           $s = $s2 = 0.0;
  215.           for ($i = $m-1; $i >= $l; $i--) {
  216.             $c3 = $c2;
  217.             $c2 = $c;
  218.             $s2 = $s;
  219.             $g  = $c * $this->e[$i];
  220.             $h  = $c * $p;
  221.             $r  = hypo($p, $this->e[$i]);
  222.             $this->e[$i+1] = $s * $r;
  223.             $s = $this->e[$i] / $r;
  224.             $c = $p / $r;
  225.             $p = $c * $this->d[$i] - $s * $g;
  226.             $this->d[$i+1] = $h + $s * ($c * $g + $s * $this->d[$i]);
  227.             // Accumulate transformation.
  228.             for ($k = 0; $k < $this->n; $k++) {
  229.               $h = $this->V[$k][$i+1];
  230.               $this->V[$k][$i+1] = $s * $this->V[$k][$i] + $c * $h;
  231.               $this->V[$k][$i] = $c * $this->V[$k][$i] - $s * $h;
  232.             }
  233.           }
  234.           $p = -$s * $s2 * $c3 * $el1 * $this->e[$l] / $dl1;
  235.           $this->e[$l] = $s * $p;
  236.           $this->d[$l] = $c * $p;
  237.           // Check for convergence.
  238.         } while (abs($this->e[$l]) > $eps * $tst1);
  239.       }
  240.       $this->d[$l] = $this->d[$l] + $f;
  241.       $this->e[$l] = 0.0;      
  242.     }      
  243.     // Sort eigenvalues and corresponding vectors.  
  244.     for ($i = 0; $i < $this->n - 1; $i++) {
  245.       $k = $i;
  246.       $p = $this->d[$i];
  247.       for ($j = $i+1; $j < $this->n; $j++) {
  248.         if ($this->d[$j] < $p) {
  249.           $k = $j;
  250.           $p = $this->d[$j];
  251.         }
  252.       }
  253.       if ($k != $i) {
  254.         $this->d[$k] = $this->d[$i];
  255.         $this->d[$i] = $p;
  256.         for ($j = 0; $j < $this->n; $j++) {
  257.           $p = $this->V[$j][$i];
  258.           $this->V[$j][$i] = $this->V[$j][$k];
  259.           $this->V[$j][$k] = $p;
  260.         }
  261.       }
  262.     }      
  263.   }
  264.  
  265.   /**
  266.   * Nonsymmetric reduction to Hessenberg form.
  267.   *
  268.   * This is derived from the Algol procedures orthes and ortran,
  269.   * by Martin and Wilkinson, Handbook for Auto. Comp.,
  270.   * Vol.ii-Linear Algebra, and the corresponding
  271.   * Fortran subroutines in EISPACK.
  272.   *  
  273.   * @access private
  274.   */
  275.   function orthes () {   
  276.     $low  = 0;
  277.     $high = $this->n-1;   
  278.     for ($m = $low+1; $m <= $high-1; $m++) {  
  279.       // Scale column.
  280.       $scale = 0.0;
  281.       for ($i = $m; $i <= $high; $i++)
  282.         $scale = $scale + abs($this->H[$i][$m-1]);
  283.       if ($scale != 0.0) {
  284.         // Compute Householder transformation.
  285.         $h = 0.0;
  286.         for ($i = $high; $i >= $m; $i--) {
  287.           $this->ort[$i] = $this->H[$i][$m-1]/$scale;
  288.           $h += $this->ort[$i] * $this->ort[$i];
  289.         }
  290.         $g = sqrt($h);
  291.         if ($this->ort[$m] > 0)
  292.           $g *= -1;
  293.         $h -= $this->ort[$m] * $g;
  294.         $this->ort[$m] -= $g;
  295.         // Apply Householder similarity transformation
  296.         // H = (I-u*u'/h)*H*(I-u*u')/h)
  297.         for ($j = $m; $j < $this->n; $j++) {
  298.           $f = 0.0;
  299.           for ($i = $high; $i >= $m; $i--)
  300.             $f += $this->ort[$i] * $this->H[$i][$j];
  301.           $f /= $h;
  302.           for ($i = $m; $i <= $high; $i++)
  303.             $this->H[$i][$j] -= $f * $this->ort[$i];
  304.         }
  305.         for ($i = 0; $i <= $high; $i++) {
  306.           $f = 0.0;
  307.           for ($j = $high; $j >= $m; $j--)
  308.             $f += $this->ort[$j] * $this->H[$i][$j];
  309.           $f = $f/$h;
  310.           for ($j = $m; $j <= $high; $j++)
  311.             $this->H[$i][$j] -= $f * $this->ort[$j];
  312.         }
  313.         $this->ort[$m] = $scale * $this->ort[$m];
  314.         $this->H[$m][$m-1] = $scale * $g;
  315.       }
  316.     }  
  317.     // Accumulate transformations (Algol's ortran).
  318.     for ($i = 0; $i < $this->n; $i++)
  319.       for ($j = 0; $j < $this->n; $j++)
  320.         $this->V[$i][$j] = ($i == $j ? 1.0 : 0.0);
  321.     for ($m = $high-1; $m >= $low+1; $m--) {
  322.       if ($this->H[$m][$m-1] != 0.0) {
  323.         for ($i = $m+1; $i <= $high; $i++)
  324.           $this->ort[$i] = $this->H[$i][$m-1];
  325.         for ($j = $m; $j <= $high; $j++) {
  326.           $g = 0.0;
  327.           for ($i = $m; $i <= $high; $i++)
  328.             $g += $this->ort[$i] * $this->V[$i][$j];
  329.           // Double division avoids possible underflow
  330.           $g = ($g / $this->ort[$m]) / $this->H[$m][$m-1];
  331.           for ($i = $m; $i <= $high; $i++)
  332.             $this->V[$i][$j] += $g * $this->ort[$i];
  333.         }
  334.       }
  335.     }
  336.   }
  337.  
  338.   /**
  339.   * Performs complex division.
  340.   * @access private
  341.   */            
  342.   function cdiv($xr, $xi, $yr, $yi) {
  343.     if (abs($yr) > abs($yi)) {
  344.       $r = $yi / $yr;
  345.       $d = $yr + $r * $yi;
  346.       $this->cdivr = ($xr + $r * $xi) / $d;
  347.       $this->cdivi = ($xi - $r * $xr) / $d;
  348.     } else {
  349.       $r = $yr / $yi;
  350.       $d = $yi + $r * $yr;
  351.       $this->cdivr = ($r * $xr + $xi) / $d;
  352.       $this->cdivi = ($r * $xi - $xr) / $d;
  353.     }
  354.   }
  355.   
  356.   /**
  357.   * Nonsymmetric reduction from Hessenberg to real Schur form.
  358.   *
  359.   * Code is derived from the Algol procedure hqr2,
  360.   * by Martin and Wilkinson, Handbook for Auto. Comp.,
  361.   * Vol.ii-Linear Algebra, and the corresponding
  362.   * Fortran subroutine in EISPACK.
  363.   *
  364.   * @access private
  365.   */
  366.   function hqr2 () {  
  367.     //  Initialize
  368.     $nn = $this->n;
  369.     $n  = $nn - 1;
  370.     $low = 0;
  371.     $high = $nn - 1;
  372.     $eps = pow(2.0, -52.0);
  373.     $exshift = 0.0;
  374.     $p = $q = $r = $s = $z = 0;
  375.     // Store roots isolated by balanc and compute matrix norm
  376.     $norm = 0.0;
  377.     for ($i = 0; $i < $nn; $i++) {
  378.       if (($i < $low) OR ($i > $high)) {
  379.         $this->d[$i] = $this->H[$i][$i];
  380.         $this->e[$i] = 0.0;
  381.       }
  382.       for ($j = max($i-1, 0); $j < $nn; $j++)
  383.         $norm = $norm + abs($this->H[$i][$j]);
  384.     }
  385.     // Outer loop over eigenvalue index
  386.     $iter = 0;
  387.     while ($n >= $low) {         
  388.       // Look for single small sub-diagonal element
  389.       $l = $n;
  390.       while ($l > $low) {
  391.         $s = abs($this->H[$l-1][$l-1]) + abs($this->H[$l][$l]);
  392.         if ($s == 0.0)
  393.           $s = $norm;
  394.         if (abs($this->H[$l][$l-1]) < $eps * $s)
  395.           break;
  396.         $l--;
  397.       }       
  398.       // Check for convergence
  399.       // One root found
  400.       if ($l == $n) {
  401.         $this->H[$n][$n] = $this->H[$n][$n] + $exshift;
  402.         $this->d[$n] = $this->H[$n][$n];
  403.         $this->e[$n] = 0.0;
  404.         $n--;
  405.         $iter = 0;
  406.        // Two roots found
  407.        } else if ($l == $n-1) {
  408.          $w = $this->H[$n][$n-1] * $this->H[$n-1][$n];
  409.          $p = ($this->H[$n-1][$n-1] - $this->H[$n][$n]) / 2.0;
  410.          $q = $p * $p + $w;
  411.          $z = sqrt(abs($q));
  412.          $this->H[$n][$n] = $this->H[$n][$n] + $exshift;
  413.          $this->H[$n-1][$n-1] = $this->H[$n-1][$n-1] + $exshift;
  414.          $x = $this->H[$n][$n];
  415.          // Real pair
  416.          if ($q >= 0) {
  417.            if ($p >= 0)
  418.              $z = $p + $z;
  419.            else
  420.              $z = $p - $z;
  421.            $this->d[$n-1] = $x + $z;
  422.            $this->d[$n] = $this->d[$n-1];
  423.            if ($z != 0.0)
  424.              $this->d[$n] = $x - $w / $z;
  425.            $this->e[$n-1] = 0.0;
  426.            $this->e[$n] = 0.0;
  427.            $x = $this->H[$n][$n-1];
  428.            $s = abs($x) + abs($z);
  429.            $p = $x / $s;
  430.            $q = $z / $s;
  431.            $r = sqrt($p * $p + $q * $q);
  432.            $p = $p / $r;
  433.            $q = $q / $r;
  434.            // Row modification
  435.            for ($j = $n-1; $j < $nn; $j++) {
  436.              $z = $this->H[$n-1][$j];
  437.              $this->H[$n-1][$j] = $q * $z + $p * $this->H[$n][$j];
  438.              $this->H[$n][$j] = $q * $this->H[$n][$j] - $p * $z;
  439.            }
  440.            // Column modification
  441.            for ($i = 0; $i <= n; $i++) {
  442.              $z = $this->H[$i][$n-1];
  443.              $this->H[$i][$n-1] = $q * $z + $p * $this->H[$i][$n];
  444.              $this->H[$i][$n] = $q * $this->H[$i][$n] - $p * $z;
  445.            }
  446.            // Accumulate transformations
  447.            for ($i = $low; $i <= $high; $i++) {
  448.              $z = $this->V[$i][$n-1];
  449.              $this->V[$i][$n-1] = $q * $z + $p * $this->V[$i][$n];
  450.              $this->V[$i][$n] = $q * $this->V[$i][$n] - $p * $z;
  451.            }
  452.          // Complex pair
  453.          } else {
  454.            $this->d[$n-1] = $x + $p;
  455.            $this->d[$n] = $x + $p;
  456.            $this->e[$n-1] = $z;
  457.            $this->e[$n] = -$z;
  458.          }
  459.          $n = $n - 2;
  460.          $iter = 0;
  461.        // No convergence yet          
  462.        } else {
  463.          // Form shift
  464.          $x = $this->H[$n][$n];
  465.          $y = 0.0;
  466.          $w = 0.0;
  467.          if ($l < $n) {
  468.            $y = $this->H[$n-1][$n-1];
  469.            $w = $this->H[$n][$n-1] * $this->H[$n-1][$n];
  470.          }
  471.          // Wilkinson's original ad hoc shift
  472.          if ($iter == 10) {
  473.            $exshift += $x;
  474.            for ($i = $low; $i <= $n; $i++)
  475.              $this->H[$i][$i] -= $x;
  476.            $s = abs($this->H[$n][$n-1]) + abs($this->H[$n-1][$n-2]);
  477.            $x = $y = 0.75 * $s;
  478.            $w = -0.4375 * $s * $s;
  479.          }
  480.          // MATLAB's new ad hoc shift
  481.          if ($iter == 30) {
  482.            $s = ($y - $x) / 2.0;
  483.            $s = $s * $s + $w;
  484.            if ($s > 0) {
  485.              $s = sqrt($s);
  486.              if ($y < $x)
  487.                $s = -$s;
  488.              $s = $x - $w / (($y - $x) / 2.0 + $s);
  489.              for ($i = $low; $i <= $n; $i++)
  490.                $this->H[$i][$i] -= $s;
  491.              $exshift += $s;
  492.              $x = $y = $w = 0.964;
  493.            }
  494.          }            
  495.          // Could check iteration count here.
  496.          $iter = $iter + 1;   
  497.          // Look for two consecutive small sub-diagonal elements
  498.          $m = $n - 2;
  499.          while ($m >= $l) {
  500.            $z = $this->H[$m][$m];
  501.            $r = $x - $z;
  502.            $s = $y - $z;
  503.            $p = ($r * $s - $w) / $this->H[$m+1][$m] + $this->H[$m][$m+1];
  504.            $q = $this->H[$m+1][$m+1] - $z - $r - $s;
  505.            $r = $this->H[$m+2][$m+1];
  506.            $s = abs($p) + abs($q) + abs($r);
  507.            $p = $p / $s;
  508.            $q = $q / $s;
  509.            $r = $r / $s;
  510.            if ($m == $l)
  511.              break;
  512.            if (abs($this->H[$m][$m-1]) * (abs($q) + abs($r)) < $eps * (abs($p) * (abs($this->H[$m-1][$m-1]) + abs($z) + abs($this->H[$m+1][$m+1]))))
  513.              break;
  514.            $m--;
  515.          }
  516.          for ($i = $m + 2; $i <= $n; $i++) {
  517.            $this->H[$i][$i-2] = 0.0;
  518.            if ($i > $m+2)
  519.              $this->H[$i][$i-3] = 0.0;
  520.          }
  521.          // Double QR step involving rows l:n and columns m:n
  522.          for ($k = $m; $k <= $n-1; $k++) {
  523.            $notlast = ($k != $n-1);
  524.            if ($k != $m) {
  525.              $p = $this->H[$k][$k-1];
  526.              $q = $this->H[$k+1][$k-1];
  527.              $r = ($notlast ? $this->H[$k+2][$k-1] : 0.0);
  528.              $x = abs($p) + abs($q) + abs($r);
  529.              if ($x != 0.0) {
  530.                $p = $p / $x;
  531.                $q = $q / $x;
  532.                $r = $r / $x;
  533.              }
  534.            }
  535.            if ($x == 0.0)
  536.              break;
  537.            $s = sqrt($p * $p + $q * $q + $r * $r);
  538.            if ($p < 0)
  539.              $s = -$s;
  540.            if ($s != 0) {
  541.              if ($k != $m)
  542.                $this->H[$k][$k-1] = -$s * $x;
  543.              else if ($l != $m)
  544.                $this->H[$k][$k-1] = -$this->H[$k][$k-1];
  545.              $p = $p + $s;
  546.              $x = $p / $s;
  547.              $y = $q / $s;
  548.              $z = $r / $s;
  549.              $q = $q / $p;
  550.              $r = $r / $p;
  551.              // Row modification
  552.              for ($j = $k; $j < $nn; $j++) {
  553.                $p = $this->H[$k][$j] + $q * $this->H[$k+1][$j];
  554.                if ($notlast) {
  555.                  $p = $p + $r * $this->H[$k+2][$j];
  556.                  $this->H[$k+2][$j] = $this->H[$k+2][$j] - $p * $z;
  557.                }
  558.                $this->H[$k][$j] = $this->H[$k][$j] - $p * $x;
  559.                $this->H[$k+1][$j] = $this->H[$k+1][$j] - $p * $y;
  560.              }
  561.              // Column modification
  562.              for ($i = 0; $i <= min($n, $k+3); $i++) {
  563.                $p = $x * $this->H[$i][$k] + $y * $this->H[$i][$k+1];
  564.                if ($notlast) {
  565.                  $p = $p + $z * $this->H[$i][$k+2];
  566.                  $this->H[$i][$k+2] = $this->H[$i][$k+2] - $p * $r;
  567.                }
  568.                $this->H[$i][$k] = $this->H[$i][$k] - $p;
  569.                $this->H[$i][$k+1] = $this->H[$i][$k+1] - $p * $q;
  570.              }
  571.              // Accumulate transformations
  572.              for ($i = $low; $i <= $high; $i++) {
  573.                $p = $x * $this->V[$i][$k] + $y * $this->V[$i][$k+1];
  574.                if ($notlast) {
  575.                  $p = $p + $z * $this->V[$i][$k+2];
  576.                  $this->V[$i][$k+2] = $this->V[$i][$k+2] - $p * $r;
  577.                }
  578.                $this->V[$i][$k] = $this->V[$i][$k] - $p;
  579.                $this->V[$i][$k+1] = $this->V[$i][$k+1] - $p * $q;
  580.              }
  581.            }  // ($s != 0)
  582.          }  // k loop
  583.        }  // check convergence
  584.     }  // while ($n >= $low)                      
  585.     // Backsubstitute to find vectors of upper triangular form
  586.     if ($norm == 0.0)
  587.       return;
  588.     for ($n = $nn-1; $n >= 0; $n--) {
  589.       $p = $this->d[$n];
  590.       $q = $this->e[$n];
  591.       // Real vector
  592.       if ($q == 0) {
  593.         $l = $n;
  594.         $this->H[$n][$n] = 1.0;
  595.         for ($i = $n-1; $i >= 0; $i--) {
  596.           $w = $this->H[$i][$i] - $p;
  597.           $r = 0.0;
  598.           for ($j = $l; $j <= $n; $j++)
  599.             $r = $r + $this->H[$i][$j] * $this->H[$j][$n];
  600.           if ($this->e[$i] < 0.0) {
  601.             $z = $w;
  602.             $s = $r;
  603.           } else {
  604.             $l = $i;
  605.             if ($this->e[$i] == 0.0) {
  606.               if ($w != 0.0)
  607.                 $this->H[$i][$n] = -$r / $w;
  608.               else
  609.                 $this->H[$i][$n] = -$r / ($eps * $norm);
  610.               // Solve real equations
  611.             } else {
  612.               $x = $this->H[$i][$i+1];
  613.               $y = $this->H[$i+1][$i];
  614.               $q = ($this->d[$i] - $p) * ($this->d[$i] - $p) + $this->e[$i] * $this->e[$i];
  615.               $t = ($x * $s - $z * $r) / $q;
  616.               $this->H[$i][$n] = $t;
  617.               if (abs($x) > abs($z))
  618.                 $this->H[$i+1][$n] = (-$r - $w * $t) / $x;
  619.               else
  620.                 $this->H[$i+1][$n] = (-$s - $y * $t) / $z;
  621.             }
  622.             // Overflow control
  623.             $t = abs($this->H[$i][$n]);
  624.             if (($eps * $t) * $t > 1) {
  625.               for ($j = $i; $j <= $n; $j++)
  626.                 $this->H[$j][$n] = $this->H[$j][$n] / $t;
  627.             }
  628.           }
  629.         }
  630.       // Complex vector
  631.       } else if ($q < 0) {
  632.         $l = $n-1;
  633.         // Last vector component imaginary so matrix is triangular
  634.         if (abs($this->H[$n][$n-1]) > abs($this->H[$n-1][$n])) {
  635.           $this->H[$n-1][$n-1] = $q / $this->H[$n][$n-1];
  636.           $this->H[$n-1][$n] = -($this->H[$n][$n] - $p) / $this->H[$n][$n-1];
  637.         } else {
  638.           $this->cdiv(0.0, -$this->H[$n-1][$n], $this->H[$n-1][$n-1] - $p, $q);
  639.           $this->H[$n-1][$n-1] = $this->cdivr;
  640.           $this->H[$n-1][$n]   = $this->cdivi;
  641.         }
  642.         $this->H[$n][$n-1] = 0.0;
  643.         $this->H[$n][$n] = 1.0;
  644.         for ($i = $n-2; $i >= 0; $i--) {
  645.           // double ra,sa,vr,vi;
  646.           $ra = 0.0;
  647.           $sa = 0.0;
  648.           for ($j = $l; $j <= $n; $j++) {
  649.             $ra = $ra + $this->H[$i][$j] * $this->H[$j][$n-1];
  650.             $sa = $sa + $this->H[$i][$j] * $this->H[$j][$n];
  651.           }
  652.           $w = $this->H[$i][$i] - $p;
  653.           if ($this->e[$i] < 0.0) {
  654.             $z = $w;
  655.             $r = $ra;
  656.             $s = $sa;
  657.           } else {
  658.             $l = $i;
  659.             if ($this->e[$i] == 0) {
  660.               $this->cdiv(-$ra, -$sa, $w, $q);
  661.               $this->H[$i][$n-1] = $this->cdivr;
  662.               $this->H[$i][$n]   = $this->cdivi;
  663.             } else {
  664.               // Solve complex equations
  665.               $x = $this->H[$i][$i+1];
  666.               $y = $this->H[$i+1][$i];
  667.               $vr = ($this->d[$i] - $p) * ($this->d[$i] - $p) + $this->e[$i] * $this->e[$i] - $q * $q;
  668.               $vi = ($this->d[$i] - $p) * 2.0 * $q;
  669.               if ($vr == 0.0 & $vi == 0.0)
  670.                 $vr = $eps * $norm * (abs($w) + abs($q) + abs($x) + abs($y) + abs($z));
  671.               $this->cdiv($x * $r - $z * $ra + $q * $sa, $x * $s - $z * $sa - $q * $ra, $vr, $vi);
  672.               $this->H[$i][$n-1] = $this->cdivr;
  673.               $this->H[$i][$n]   = $this->cdivi;
  674.               if (abs($x) > (abs($z) + abs($q))) {
  675.                 $this->H[$i+1][$n-1] = (-$ra - $w * $this->H[$i][$n-1] + $q * $this->H[$i][$n]) / $x;
  676.                 $this->H[$i+1][$n] = (-$sa - $w * $this->H[$i][$n] - $q * $this->H[$i][$n-1]) / $x;
  677.               } else {
  678.                 $this->cdiv(-$r - $y * $this->H[$i][$n-1], -$s - $y * $this->H[$i][$n], $z, $q);
  679.                 $this->H[$i+1][$n-1] = $this->cdivr;
  680.                 $this->H[$i+1][$n]   = $this->cdivi;
  681.               }
  682.             }
  683.             // Overflow control
  684.             $t = max(abs($this->H[$i][$n-1]),abs($this->H[$i][$n]));
  685.             if (($eps * $t) * $t > 1) {
  686.               for ($j = $i; $j <= $n; $j++) {
  687.                 $this->H[$j][$n-1] = $this->H[$j][$n-1] / $t;
  688.                 $this->H[$j][$n]   = $this->H[$j][$n] / $t;
  689.               }
  690.             }
  691.           } // end else
  692.         } // end for
  693.       } // end else for complex case
  694.     } // end for            
  695.     // Vectors of isolated roots
  696.     for ($i = 0; $i < $nn; $i++) {
  697.       if ($i < $low | $i > $high) {
  698.         for ($j = $i; $j < $nn; $j++)
  699.           $this->V[$i][$j] = $this->H[$i][$j];
  700.       }
  701.     }
  702.     // Back transformation to get eigenvectors of original matrix
  703.     for ($j = $nn-1; $j >= $low; $j--) {
  704.       for ($i = $low; $i <= $high; $i++) {
  705.         $z = 0.0;
  706.         for ($k = $low; $k <= min($j,$high); $k++)
  707.           $z = $z + $this->V[$i][$k] * $this->H[$k][$j];
  708.         $this->V[$i][$j] = $z;
  709.       }
  710.     }                                     
  711.   } // end hqr2
  712.   
  713.   /**
  714.   * Constructor: Check for symmetry, then construct the eigenvalue decomposition
  715.   * @access public
  716.   * @param A  Square matrix
  717.   * @return Structure to access D and V.
  718.   */
  719.   function EigenvalueDecomposition($Arg) {
  720.     $this->A = $Arg->getArray();
  721.     $this->n = $Arg->getColumnDimension();
  722.     $this->V = array();
  723.     $this->d = array();
  724.     $this->e = array();    
  725.     $issymmetric = true;
  726.     for ($j = 0; ($j < $this->n) & $issymmetric; $j++)
  727.       for ($i = 0; ($i < $this->n) & $issymmetric; $i++)
  728.         $issymmetric = ($this->A[$i][$j] == $this->A[$j][$i]);
  729.     if ($issymmetric) {
  730.        $this->V = $this->A;
  731.        // Tridiagonalize.
  732.        $this->tred2();
  733.        // Diagonalize.
  734.        $this->tql2();
  735.      } else {
  736.        $this->H = $this->A;
  737.        $this->ort = array();
  738.        // Reduce to Hessenberg form.
  739.        $this->orthes();  
  740.        // Reduce Hessenberg to real Schur form.
  741.        $this->hqr2();
  742.      }
  743.   }
  744.  
  745.   /**
  746.   * Return the eigenvector matrix
  747.   * @access public
  748.   * @return V 
  749.   */
  750.   function getV() {
  751.     return new Matrix($this->V, $this->n, $this->n);
  752.   }
  753.  
  754.   /**
  755.   * Return the real parts of the eigenvalues
  756.   * @access public
  757.   * @return real(diag(D)) 
  758.   */
  759.   function getRealEigenvalues() {
  760.     return $this->d;
  761.   }
  762.  
  763.   /**
  764.   * Return the imaginary parts of the eigenvalues
  765.   * @access public
  766.   * @return imag(diag(D)) 
  767.   */
  768.   function getImagEigenvalues() {
  769.     return $this->e;
  770.   }
  771.  
  772.   /**
  773.   * Return the block diagonal eigenvalue matrix
  774.   * @access public
  775.   * @return D 
  776.   */
  777.   function getD() {
  778.     for ($i = 0; $i < $this->n; $i++) {
  779.       $D[$i] = array_fill(0, $this->n, 0.0);
  780.       $D[$i][$i] = $this->d[$i];
  781.       if ($this->e[$i] == 0)
  782.       continue;
  783.     $o = ($this->e[$i] > 0) ? $i + 1 : $i - 1;
  784.     $D[$i][$o] = $this->e[$i];
  785.     }
  786.     return new Matrix($D);
  787.   }         
  788. }

Documentation generated on Mon, 05 Jan 2009 20:36:52 +0100 by phpDocumentor 1.4.1