C# .Net Program to Find a Function Minimum

Using a Direct Search Method

by Namir Shammas

The following program calculates the minimum point of a multi-variable function using direct search method.

Click here to download a ZIP file containing the project files for this program.

The pseudo-code for this algorithm is:

Given

  1. The number of variables N
  2. The array of initial values X
  3. The array of initial search steps S
  4. The array of minimum search steps M

Method

  1. F0 = F(X)
  2. For I = 1 to N
  3. Let X0 = X(I)
  4. X(I) = X(I) + S(I)
  5. If F(X) < F0 then go to step 10
  6. Let X(I) = X0 - S(I)
  7. If F(X) < F0 then go to step 11
  8. X(I) = X0
  9. If S(I) <= M(I) then Let S(I) = S(I) / 10
  10. Go to step 12
  11. F0 = F(X)
  12. Next I
  13. Let S0 = 0
  14. For I = 1 to N
  15. If S(I) >= M(I) then S0 = 1
  16. Next I
  17. If S0 > 0 Then go to step 2
  18. Display array X()

The program prompts you to either use the predefined default input values or to enter the following for each variable:

1. Guess for the minimum point.

2. Initial search step value.

3. The minimum search step value.

In case you choose the default input values, the program displays these values and proceeds to find the optimum point. In the case you select being prompted, the program displays the name of each input variable along with its default value. You can then either enter a new value or simply press Enter to use the default value. This approach allows you to quickly and efficiently change only a few input values if you so desire.

The program displays the following results:

1. The coordinates of the minimum value.

2. The minimum function value.

3. The number of iterations

Here is a sample session to find the minimum of function:

f(x) = 10 + (x1-2)^2 + (x2+5)^2

Using initial values of 0, initial search steps of 0.1 for both variables, and minimum search steps of 1e-5 for both variables. Here is the sample console screen:

Here is the BASIC listing for the main module. The module contains several test functions:

using System.Diagnostics;
using System.Data;
using System.Collections;
using Microsoft.VisualBasic;
using System.Collections.Generic;
using System;

namespace Optim_DirectSearch1
{
	sealed class Module1
	{
		
		static public void Main()
		{
			int nNumVars = 2;
			double[] fX = new double[] { 0, 0 };
			double[] fParam = new double[] { 0, 0 };
			double[] fStepSize = new double[] { 0.1, 0.1 };
			double[] fMinStepSize = new double[] { 0.00001, 0.00001 };
			int nIter = 0;
			int i;
			double fBestF;
			string sAnswer;
			CDirectSearch oOpt;
			MyFxDelegate MyFx = new MyFxDelegate( Fx1);
			SayFxDelegate SayFx = new SayFxDelegate( SayFx1);
			
			oOpt = new CDirectSearch();
			
			Console.WriteLine("Direct Search Optimization");
			Console.WriteLine("Finding the minimu of function:");
			Console.WriteLine(SayFx());
			Console.Write("Use default input values? (Y/N) ");
			sAnswer = Console.ReadLine();
			if (sAnswer.ToUpper() == "Y")
			{
				for (i = 0; i < nNumVars; i++)
				{
					Console.WriteLine("X({0}) = {1}", i + 1, fX[i]);
					Console.WriteLine("Step Size({0}) = {1}", i + 1, fStepSize[i]);
					Console.WriteLine("Min Step Size({0}) = {1}", i + 1, fMinStepSize[i]);
				}
			}
			else
			{
				for (i = 0; i < nNumVars; i++)
				{
					fX[i] = GetIndexedDblInput("X", i + 1, fX[i]);
					fStepSize[i] = GetIndexedDblInput("Initial step size(", i + 1, fStepSize[i]);
					fMinStepSize[i] = GetIndexedDblInput("Minimum step size", i + 1, fMinStepSize[i]);
				}
			}
			
			Console.WriteLine("******** FINAL RESULTS *************");
			fBestF = oOpt.DirectSearch(nNumVars, ref fX, ref fParam, ref fStepSize, ref fMinStepSize, ref nIter, MyFx);
			Console.WriteLine("Optimum at");
			for (i = 0; i < nNumVars; i++)
			{
				Console.WriteLine("X({0}) = {1}", i + 1, fX[i]);
			}
			Console.WriteLine("Function value = {0}", fBestF);
			Console.WriteLine("Number of iterations = {0}", nIter);
			Console.WriteLine();
			Console.Write("Press Enter to end the program ...");
			Console.ReadLine();
		}
		
		static public double GetDblInput(string sPrompt, double fDefInput)
		{
			string sInput;
			
			Console.Write("{0}? ({1}): ", sPrompt, fDefInput);
			sInput = Console.ReadLine();
			if (sInput.Trim(null).Length > 0)
			{
				return double.Parse(sInput);
			}
			else
			{
				return fDefInput;
			}
		}
		
		static public double GetIndexedDblInput(string sPrompt, int nIndex, double fDefInput)
		{
			string sInput;
			
			Console.Write("{0}({1})? ({2}): ", sPrompt, nIndex, fDefInput);
			sInput = Console.ReadLine();
			if (sInput.Trim(null).Length > 0)
			{
				return double.Parse(sInput);
			}
			else
			{
				return fDefInput;
			}
		}

        static public string SayFx1()
        {
            return "F(X) = 10 + (X(1) - 2) ^ 2 + (X(2) + 5) ^ 2";
        }

        static public double Fx1(int N, ref double[] X, ref double[] fParam)
        {
            return 10 + Math.Pow(X[0] - 2, 2) + Math.Pow(X[1] + 5, 2);
        }

        static public string SayFx2()
        {
            return "F(X) = 100 * (X(1) - X(2) ^ 2) ^ 2 + (X(2) - 1) ^ 2";
        }

        static public double Fx2(int N, ref double[] X, ref double[] fParam)
        {
            return Math.Pow(100 * (X[0] - X[1] * X[1]), 2) + Math.Pow((X[1] - 1), 2);
        }

        static public string SayFx3()
        {
            return "F(X) = X(1) - X(2) + 2 * X(1) ^ 2 + 2 * X(1) * X(2) + X(2) ^ 2";
        }

        static public double Fx3(int N, ref double[] X, ref double[] fParam)
        {
            return X[0] - X[1] + 2 * X[0] * X[0] + 2 * X[0] * X[1] + X[1] * X[1];
        }

		
		static public string SayFxFit()
		{
			return "Fitting Yd = 11 - 10 * Exp(-0.75 * Xd) + 8 * Exp(-0.35 * Xd)";
		}
		
		static public double FxFit(int N, ref double[] X, ref double[] fParam)
		{
			const int M = 100;
			
			int i;
			double Xd;
			double Yd;
			double Yd2;
			double SumSqr;
			
			SumSqr = 0;
			for (i = 0; i <= M - 1; i++)
			{
				Xd = 0.1 *(i + 1);
				Yd = 11 - 10 * Math.Exp(- 0.75 * Xd) + 8 * Math.Exp(- 0.35 * Xd);
				Yd2 = X[0] - X[1] * Math.Exp(- X[2] * Xd) + X[3] * Math.Exp(- X[4] * Xd);
				SumSqr = Math.Pow(SumSqr +(Yd - Yd2), 2);
			}
			return SumSqr;
		}
	}
	
}

Notice that the user-defined functions have accompanying helper functions to display the mathematical expression of the function being optimized. For example, function Fx1 has the helper function SayFx1 to list the function optimized in Fx1. Please observe the following rules::

The program uses the following class to optimize the objective function:

using System.Diagnostics;
using System.Data;
using System.Collections;
using Microsoft.VisualBasic;
using System.Collections.Generic;
using System;

namespace Optim_DirectSearch1
{
	public delegate double MyFxDelegate(int nNumVars, ref double[] fX, ref double[] fParam);
	public delegate string SayFxDelegate();
	
	public class CDirectSearch
	{
		
		
		public double DirectSearch(int nNumVars, ref double[] fX, ref double[] fParam, ref double[] fStepSize, ref double[] fMinStepSize, ref int nIter, MyFxDelegate MyFx)
		{
			const int StepReduceFactor = 10;
			
			double fBestF, F, fXX;
			int i;
			bool bStop, bFound;
			
            fBestF = MyFx(nNumVars, ref fX, ref fParam);
			
			nIter = 0;
			do
			{
				nIter++;
				
				for (i = 0; i < nNumVars; i++)
				{
					bFound = false;
					do
					{
						fXX = fX[i];
						fX[i] = fXX + fStepSize[i];
                        F = MyFx(nNumVars, ref fX, ref fParam);
						if (F < fBestF)
						{
							fBestF = F;
							bFound = true;
						}
						else
						{
							fX[i] = fXX - fStepSize[i];
                            F = MyFx(nNumVars, ref fX, ref fParam);
							if (F < fBestF)
							{
								fBestF = F;
								bFound = true;
							}
							else
							{
								fX[i] = fXX;
								if (! bFound)
								{
									fStepSize[i] = fStepSize[i] / StepReduceFactor;
								}
								break;
							}
						}
					} while (true);
				}
				
				bStop = true;
				for (i = 0; i < nNumVars; i++)
				{
					if (fStepSize[i] >= fMinStepSize[i])
					{
						bStop = false;
						break;
					}
				}
				
			} while (!bStop);
			
			return fBestF;
		}
	}
}

BACK

Copyright (c) Namir Shammas. All rights reserved.