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

Source for file LevenbergMarquardt.php

Documentation is available at LevenbergMarquardt.php

  1. <?php
  2.  
  3. // Levenberg-Marquardt in PHP 
  4.  
  5. // http://www.idiom.com/~zilla/Computer/Javanumeric/LM.java
  6.  
  7.  
  8.   /**
  9.   * Calculate the current sum-squared-error
  10.   *
  11.   * Chi-squared is the distribution of squared Gaussian errors,
  12.   * thus the name.
  13.   *
  14.   * @param double[][] $x 
  15.   * @param double[] $a 
  16.   * @param double[] $y, 
  17.   * @param double[] $s, 
  18.   * @param object $f 
  19.   */
  20.   function chiSquared($x, $a, $y, $s, $f) {
  21.  
  22.     $npts = count($y);
  23.  
  24.     $sum = 0.0;
  25.  
  26.     for($i = 0; $i < $npts; $i++ ) {
  27.       $d = $y[$i] - $f->val($x[$i], $a);
  28.       $d = $d / $s[$i];
  29.       $sum = $sum + ($d*$d);
  30.     }
  31.  
  32.     return $sum;
  33.  
  34.   }
  35.  
  36.  
  37.   /**
  38.   * Minimize E = sum {(y[k] - f(x[k],a)) / s[k]}^2
  39.   * The individual errors are optionally scaled by s[k].
  40.   * Note that LMfunc implements the value and gradient of f(x,a),
  41.   * NOT the value and gradient of E with respect to a!
  42.   * 
  43.   * @param x array of domain points, each may be multidimensional
  44.   * @param y corresponding array of values
  45.   * @param a the parameters/state of the model
  46.   * @param vary false to indicate the corresponding a[k] is to be held fixed
  47.   * @param s2 sigma^2 for point i
  48.   * @param lambda blend between steepest descent (lambda high) and
  49.   *     jump to bottom of quadratic (lambda zero).
  50.   *      Start with 0.001.
  51.   * @param termepsilon termination accuracy (0.01)
  52.   * @param maxiter    stop and return after this many iterations if not done
  53.   * @param verbose    set to zero (no prints), 1, 2
  54.   *
  55.   * @return the new lambda for future iterations.
  56.   *   Can use this and maxiter to interleave the LM descent with some other
  57.   *   task, setting maxiter to something small.
  58.   */
  59.   function solve($x, $a, $y, $s, $vary, $f, $lambda, $termepsilon, $maxiter, $verbose) {
  60.  
  61.     $npts = count($y);
  62.     $nparm = count($a);
  63.  
  64.     if ($verbose > 0) {
  65.       print("solve x[".count($x)."][".count($x[0])."]");
  66.       print(" a[".count($a)."]");
  67.       println(" y[".count(length)."]");
  68.     }
  69.  
  70.     $e0 = $this->chiSquared($x, $a, $y, $s, $f);
  71.  
  72.     //double lambda = 0.001;
  73.     $done = false;
  74.  
  75.     // g = gradient, H = hessian, d = step to minimum
  76.     // H d = -g, solve for d
  77.     $H = array();
  78.     $g = array();
  79.     
  80.     //double[] d = new double[nparm];
  81.  
  82.     $oos2 = array();
  83.     
  84.     for($i = 0; $i < $npts; $i++)  $oos2[$i] = 1./($s[$i]*$s[$i]);
  85.  
  86.     $iter = 0;
  87.     $term = 0;    // termination count test
  88.  
  89.     do {
  90.       ++$iter;
  91.  
  92.       // hessian approximation
  93.       for( $r = 0; $r < $nparm; $r++ ) {
  94.           for( $c = 0; $c < $nparm; $c++ ) {
  95.             for( $i = 0; $i < $npts; $i++ ) {
  96.               if ($i == 0) $H[$r][$c] = 0.;
  97.               $xi = $x[$i];
  98.               $H[$r][$c] += ($oos2[$i] * $f->grad($xi, $a, $r) * $f->grad($xi, $a, $c));
  99.             }  //npts
  100.           } //c
  101.       } //r
  102.  
  103.       // boost diagonal towards gradient descent
  104.       for( $r = 0; $r < $nparm; $r++ )
  105.           $H[$r][$r] *= (1. + $lambda);
  106.  
  107.       // gradient
  108.       for( $r = 0; $r < $nparm; $r++ ) {
  109.           for( $i = 0; $i < $npts; $i++ ) {
  110.             if ($i == 0) $g[$r] = 0.;
  111.             $xi = $x[$i];
  112.             $g[$r] += ($oos2[$i] * ($y[$i]-$f->val($xi,$a)) * $f->grad($xi, $a, $r));
  113.           }
  114.       } //npts
  115.  
  116.       // scale (for consistency with NR, not necessary)
  117.       if ($false) {
  118.           for( $r = 0; $r < $nparm; $r++ ) {
  119.             $g[$r] = -0.5 * $g[$r];
  120.             for( $c = 0; $c < $nparm; $c++ ) {
  121.               $H[$r][$c] *= 0.5;
  122.             }
  123.           }
  124.       }
  125.  
  126.       // solve H d = -g, evaluate error at new location
  127.       //double[] d = DoubleMatrix.solve(H, g);
  128.       double[] d = (new Matrix(H)).lu().solve(new Matrix(g, nparm)).getRowPackedCopy();
  129.       //double[] na = DoubleVector.add(a, d);
  130.       double[] na = (new Matrix(a, nparm)).plus(new Matrix(d, nparm)).getRowPackedCopy();
  131.       double e1 = chiSquared(x, na, y, s, f);
  132.  
  133.       if (verbose > 0) {
  134.           System.out.println("\n\niteration "+iter+" lambda = "+lambda);
  135.           System.out.print("a = ");
  136.         (new Matrix(a, nparm)).print(10, 2);
  137.           if (verbose > 1) {
  138.           System.out.print("H = "); 
  139.           (new Matrix(H)).print(10, 2);
  140.           System.out.print("g = "); 
  141.           (new Matrix(g, nparm)).print(10, 2);
  142.           System.out.print("d = "); 
  143.           (new Matrix(d, nparm)).print(10, 2);
  144.           }
  145.           System.out.print("e0 = " + e0 + ": ");
  146.           System.out.print("moved from ");
  147.         (new Matrix(a, nparm)).print(10, 2);
  148.           System.out.print("e1 = " + e1 + ": ");
  149.           if (e1 < e0) {
  150.             System.out.print("to ");
  151.           (new Matrix(na, nparm)).print(10, 2);
  152.           } else {
  153.             System.out.println("move rejected");
  154.           }
  155.       }
  156.  
  157.       // termination test (slightly different than NR)
  158.       if (Math.abs(e1-e0) > termepsilon) {
  159.           term = 0;
  160.       } else {
  161.           term++;
  162.           if (term == 4) {
  163.             System.out.println("terminating after " + iter + " iterations");
  164.             done = true;
  165.           }
  166.       }
  167.       if (iter >= maxiter) done = true;
  168.  
  169.       // in the C++ version, found that changing this to e1 >= e0
  170.       // was not a good idea.  See comment there.
  171.       //
  172.       if (e1 > e0 || Double.isNaN(e1)) { // new location worse than before
  173.           lambda *= 10.;
  174.       } else {        // new location better, accept new parameters
  175.           lambda *= 0.1;
  176.           e0 = e1;
  177.           // simply assigning a = na will not get results copied back to caller
  178.           for( int i = 0; i < nparm; i++ ) {
  179.             if (vary[i]) a[i] = na[i];
  180.           }
  181.       }
  182.     } while(!$done);
  183.  
  184.     return $lambda;
  185.  
  186.   } //solve
  187.  
  188. }
  189.  
  190. ?>

Documentation generated on Wed, 22 Apr 2009 09:00:27 +0200 by phpDocumentor 1.4.1