///////////////////////////////////////////////////////////////////////////
//                                                                       //
// Program file name: Minimum.java                                       //
//                                                                       //
// © Tao Pang 2006                                                       //
//                                                                       //
// Last modified: April 17, 2012                                         //
//                                                                       //
// (1) This Java program is part of the book, "An Introduction to        //
//     Computational Physics, 2nd Edition," written by Tao Pang and      //
//     published by Cambridge University Press on January 19, 2006.      //
//                                                                       //
// (2) No warranties, express or implied, are made for this program.     //
//                                                                       //
///////////////////////////////////////////////////////////////////////////

// An example of searching a minimum of a multivariable
// function through the steepest-descent method.

import java.lang.*;
public class Minimum {
  public static void main(String argv[]) {
  double del = 1e-6, a = 0.1;
    double x[] = new double[2];
    x[0] = 0.1;
    x[1] =  -1;
    steepestDescent(x, a, del);
    System.out.println("The minimum is at"
      + " x= " + x[0] +", y= " +x[1]);
  }

// Method to carry out the steepest-descent search.

  public static void steepestDescent(double x[],
    double a, double del) {
    int n = x.length;
    double h = 1e-6;
    double g0 = g(x);
    double fi[] = new double[n];
    fi = f(x, h);
    double dg = 0;
    for (int i=0; i<n; ++i) dg += fi[i]*fi[i];
    dg = Math.sqrt(dg);
    double b = a/dg;
    while (dg > del) {
      for (int i=0; i<n; ++i) x[i] -= b*fi[i];
      h /= 2;
      fi = f(x, h);
      dg = 0;
      for (int i=0; i<n; ++i) dg += fi[i]*fi[i];
      dg = Math.sqrt(dg);
      b  = a/dg;
      double g1 = g(x);
      if (g1 > g0) a /= 2;
      else g0 = g1;
    }
  }

// Method to provide function f = gradient g(x).

  public static double[] f(double x[], double h) {
    int n = x.length;
    double z[] = new double[n];
    double g0 = g(x);
    for (int i=0; i<n; ++i) {
      double y[] = (double[]) x.clone();
      y[i] += h;
      z[i] = (g(y)-g0)/h;
    }
    return z;
  }

// Method to provide function g(x).

  public static double g(double x[]) {
    return (x[0]-1)*(x[0]-1)*Math.exp(-x[1]*x[1])
      +x[1]*(x[1]+2)*Math.exp(-2*x[0]*x[0]);
  }
}
