//******************************************************************************
//
// File:    MDMinimizationDownhillSimplex.java
// Package: edu.rit.numeric
// Unit:    Class edu.rit.numeric.MDMinimizationDownhillSimplex
//
// This Java source file is copyright (C) 2010 by Alan Kaminsky. All rights
// reserved. For further information, contact the author, Alan Kaminsky, at
// ark@cs.rit.edu.
//
// This Java source file is part of the Parallel Java Library ("PJ"). PJ is free
// software; you can redistribute it and/or modify it under the terms of the GNU
// General Public License as published by the Free Software Foundation; either
// version 3 of the License, or (at your option) any later version.
//
// PJ is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU General Public License for more details.
//
// A copy of the GNU General Public License is provided in the file gpl.txt. You
// may also obtain a copy of the GNU General Public License on the World Wide
// Web at http://www.gnu.org/licenses/gpl.html.
//
//******************************************************************************

package edu.rit.numeric;

import static java.lang.Math.*;

import java.util.Arrays;

/**
 * Class MDMinimizationDownhillSimplex finds a minimum of a multidimensional
 * function using the downhill simplex method of Nelder and Mead. The function
 * has N inputs and is represented by an object that implements interface
 * {@linkplain MDFunction}. The minimize() method finds a (local)
 * minimum in the function. The input to the minimize() method is an
 * N-dimensional simplex, namely a group of N+1 points in
 * N dimensions, each point different from all the others. Typically, one
 * point of the simplex is an initial guess for the solution, and the other
 * points of the simplex are perturbations of the initial guess. The
 * minimize() method evaluates the function at each point of the
 * simplex and moves the simplex through N-dimensional space to minimize
 * the smallest function value of any point in the simplex, stopping when the
 * relative difference between the smallest and largest function values of any
 * points in the simplex falls below a tolerance. The output from the
 * minimize() method is the final simplex, with the
 * smallest-function-value point at position 0. The inputs to and outputs from
 * the minimize() method are stored in the fields of an instance of
 * class MDMinimizationDownhillSimplex.
 *
 * @author  Alan Kaminsky
 * @version 05-Oct-2010
 */
public class MDMinimizationDownhillSimplex
	{

// Exported data members.

	/**
	 * The multidimensional function to be minimized.
	 */
	public final MDFunction fcn;

	/**
	 * The number of function arguments.
	 */
	public final int N;

	/**
	 * The simplex. This is an N+1-element array of N-element
	 * points. On input to the minimize() method, x contains a
	 * simplex with an initial estimate of the solution. On output from the
	 * minimize() method, x contains the final simplex, and
	 * x[0] contains the solution, namely the simplex point with the
	 * smallest function value.
	 */
	public final double[][] x;

	/**
	 * Function values of the simplex points. An N+1-element array. On
	 * output from the minimize() method, f[i] contains the
	 * function value for simplex point x[i], 0 ≤ i ≤
	 * N−1. In particular, f[0] contains the minimized
	 * function value for the solution.
	 */
	public final double[] f;

	/**
	 * Tolerance. An input to the minimize() method. Must be > 0.
	 * Termination occurs when the relative difference between the smallest and
	 * largest function values of points in the simplex is less than the
	 * tolerance. The default tolerance is 1×10−6.
	 */
	public double tol = 1.0e-6;

	/**
	 * Debug flag. An input to the minimize() method. If true, the
	 * subclassDebug() method is called at the beginning of every
	 * iteration. The default setting is false.
	 */
	public boolean debug;

// Hidden data members.

	// Indexes of simplex points with largest, second-largest, and smallest
	// function values.
	private int i_max;
	private int i_2ndmax;
	private int i_min;

	// Number of function evaluations.
	private int evalCount;

	// Sum of the simplex points.
	private double[] x_sum;

	// Trial simplex point, and its function value.
	private double[] x_trial;
	private double f_trial;

	// Maximum number of function evaluations allowed.
	private static final int MAXEVAL = 5000;

// Exported constructors.

	/**
	 * Construct a new multidimensional minimization object for the given
	 * function. Field fcn is set to theFunction. Field
	 * N is set by calling the function's argumentLength()
	 * method. Field x, the simplex, is allocated with the proper size;
	 * all simplex points are initially 0. Field f, the function
	 * values, is allocated with the proper size.
	 *
	 * @param  theFunction  Multidimensional function to be minimized.
	 *
	 * @exception  NullPointerException
	 *     (unchecked exception) Thrown if theFunction is null.
	 */
	public MDMinimizationDownhillSimplex
		(MDFunction theFunction)
		{
		fcn = theFunction;
		N = theFunction.argumentLength();
		x = new double [N + 1] [N];
		f = new double [N + 1];
		x_sum = new double [N];
		x_trial = new double [N];
		}

// Exported operations.

	/**
	 * Set the simplex to the given point plus the same perturbation along each
	 * dimension. The field x[0] is set to x; the field
	 * x[1] is set to x with delta added to element
	 * 0; the field x[2] is set to x with delta added
	 * to element 1; and so on.
	 *
	 * @param  x      Simplex point. Must be an N-element array.
	 * @param  delta  Perturbation along every dimension. Must be nonzero.
	 */
	public void setSimplex
		(double[] x,
		 double delta)
		{
		System.arraycopy (x, 0, this.x[0], 0, N);
		for (int i = 0; i < N; ++ i) this.x[i+1][i] += delta;
		}

	/**
	 * Set the simplex to the given point plus a different perturbation along
	 * each dimension. The field x[0] is set to x; the field
	 * x[1] is set to x with delta[0] added to
	 * element 0; the field x[2] is set to x with
	 * delta[1] added to element 1; and so on.
	 *
	 * @param  x      Simplex point. Must be an N-element array.
	 * @param  delta  Perturbations along each dimension. All must be nonzero.
	 */
	public void setSimplex
		(double[] x,
		 double[] delta)
		{
		System.arraycopy (x, 0, this.x[0], 0, N);
		for (int i = 0; i < N; ++ i) this.x[i+1][i] += delta[i];
		}

	/**
	 * Minimize the multidimensional function. On input, the field x
	 * must be filled in with an initial simplex, and the field tol
	 * must be set to the desired tolerance. On output, the field x
	 * contains the solution. For further information, see the documentation for
	 * each field.
	 *
	 * @exception  IllegalArgumentException
	 *     (unchecked exception) Thrown if tol ≤ 0.
	 * @exception  TooManyIterationsException
	 *     (unchecked exception) Thrown if too many function evaluations (5,000)
	 *     occurred without finding a minimum.
	 */
	public void minimize()
		{
		// Verify preconditions.
		if (tol <= 0.0)
			{
			throw new IllegalArgumentException
				("MDMinimizationDownhillSimplex.minimize(): tol = "+tol+
				 " illegal");
			}

		// Compute function values for initial simplex.
		for (int i = 0; i <= N; ++ i)
			{
			f[i] = fcn.f (x[i]);
			}
		evalCount = N + 1;

		// Compute sum of initial simplex points.
		compute_x_sum();

		// Iterate until solution is found or too many function evaluations.
		for (int iter = 1; ; ++ iter)
			{
			if (debug) subclassDebug (iter, evalCount);

			// Find largest, second-largest, and smallest simplex points.
			if (f[0] > f[1])
				{
				i_max = 0;
				i_2ndmax = i_min = 1;
				}
			else
				{
				i_max = 1;
				i_2ndmax = i_min = 0;
				}
			for (int i = 2; i <= N; ++ i)
				{
				if (f[i] > f[i_max])
					{
					i_2ndmax = i_max;
					i_max = i;
					}
				else if (f[i] > f[i_2ndmax])
					{
					i_2ndmax = i;
					}
				if (f[i] < f[i_min])
					{
					i_min = i;
					}
				}

			// Compute relative difference between largest and smallest simplex
			// points and check for termination.
			if (reldif (f[i_max], f[i_min]) < tol)
				{
				// We're done. Put solution in position 0.
				swap_x (0, i_min);
				swap_f (0, i_min);
				return;
				}

			// Check for too many function evaluations.
			if (evalCount >= MAXEVAL)
				{
				throw new TooManyIterationsException
					("MDMinimizationDownhillSimplex.minimize(): Too many function evaluations ("+
					 MAXEVAL+") with no solution");
				}

			// Try reflecting largest point through opposite face of simplex.
			compute_x_trial (-1.0);

			// If reflection gave a new smallest point, try an additional
			// expansion.
			if (f_trial <= f[i_min])
				{
				compute_x_trial (2.0);
				}

			// If reflection was worse than second-largest point, try a
			// one-dimensional contraction of largest point.
			else if (f_trial >= f[i_2ndmax])
				{
				double prev_f_trial = f_trial;
				compute_x_trial (0.5);

				// If one-dimensional contraction gave no improvement, try an
				// all-dimensional contraction toward smallest point.
				if (f_trial >= prev_f_trial)
					{
					double[] x_min = x[i_min];
					for (int i = 0; i <= N; ++ i)
						{
						if (i != i_min)
							{
							double[] x_i = x[i];
							for (int j = 0; j < N; ++ j)
								{
								x_i[j] = 0.5*(x_i[j] + x_min[j]);
								}
							f[i] = fcn.f (x_i);
							}
						}
					evalCount += N;
					compute_x_sum();
					}
				}
			}
		}

// Hidden operations.

	/**
	 * Do debugging. If the debug field is true, the
	 * subclassDebug() method is called at the beginning of every
	 * iteration. The fields of this object contain the current state of the
	 * algorithm. The fields of this object must not be altered.
	 * 

* The default implementation of the subclassDebug() method does * nothing. A subclass can override the subclassDebug() method to * do something, such as print debugging information. * * @param iter Iteration number. * @param eval Number of function evaluations. */ protected void subclassDebug (int iter, int eval) { } /** * Computes the sum of the simplex points and puts the answer in x_sum. */ private void compute_x_sum() { Arrays.fill (x_sum, 0.0); for (int i = 0; i <= N; ++ i) { double[] x_i = x[i]; for (int j = 0; j < N; ++ j) { x_sum[j] += x_i[j]; } } } /** * Returns the relative difference of the arguments. */ private double reldif (double a, double b) { return 2.0*abs(a - b)/(abs(a) + abs(b) + 1.0e-10); } /** * Swap the elements of the x array at the given indexes. */ private void swap_x (int i, int j) { double[] tmp = x[i]; x[i] = x[j]; x[j] = tmp; } /** * Swap the elements of the f array at the given indexes. */ private void swap_f (int i, int j) { double tmp = f[i]; f[i] = f[j]; f[j] = tmp; } /** * Computes a trial simplex point by extrapolating the largest point across * the opposite face of the simplex by the given factor. Results are stored * in fields x_trial and f_trial. If the trial simplex point's function * value is less than the largest simplex point's function value, replaces * the largest simplex point with the trial simplex point. */ private void compute_x_trial (double factor) { double a = (1.0 - factor)/N; double b = a - factor; double[] x_max = x[i_max]; for (int i = 0; i < N; ++ i) { x_trial[i] = a*x_sum[i] - b*x_max[i]; } f_trial = fcn.f (x_trial); ++ evalCount; if (f_trial < f[i_max]) { for (int i = 0; i < N; ++ i) { x_sum[i] = x_sum[i] - x_max[i] + x_trial[i]; x_max[i] = x_trial[i]; } f[i_max] = f_trial; } } // Unit test main program. // /** // * Unit test main program. // */ // public static void main // (String[] args) // { // MDFunction fcn = new MDFunction() // { // public int argumentLength() // { // return 2; // } // public double f (double[] x) // { // return 2.0 - gaussian (x[0]) - gaussian (x[1]); // } // private double gaussian (double x) // { // double d = x - 0.5; // return exp (-d*d); // } // }; // MDMinimizationDownhillSimplex minimizer = // new MDMinimizationDownhillSimplex (fcn) // { // protected void subclassDebug (int iter, int eval) // { // System.out.printf ("--------%n"); // System.out.printf ("Iteration %d, %d evaluations%n", // iter, eval); // for (int i = 0; i <= N; ++ i) // { // System.out.printf ("x[%d] =", i); // for (int j = 0; j < N; ++ j) // { // System.out.printf (" %g", x[i][j]); // } // System.out.printf (", f[%d] = %g%n", i, f[i]); // } // } // }; // minimizer.debug = true; // minimizer.setSimplex (new double[] {0.0, 0.0}, 0.1); // minimizer.minimize(); // System.out.printf ("--------%n"); // System.out.printf ("Solution: x ="); // for (int j = 0; j < minimizer.N; ++ j) // { // System.out.printf (" %g", minimizer.x[0][j]); // } // System.out.printf (", f = %g%n", minimizer.f[0]); // } }

Ads help maintain this website.