Search code examples
c#numerical-analysismath.net

How to use the Nelder Meade Simplex algorithm in mathdotnet for function maximization


In my C# program I have a dataset where each data point consists of:

  • a stimulus intensity (intensity) as x-coordinate
  • the percentage of correct response (percentageCorrect) to stimulus as y-coordinate

When the intensity is low percentageCorrect is low. When the intensity is high the percentageCorrect is high. The function graph is an S-shaped curve as the percentageCorrect reaches an asymptote at low and high ends.

I am trying to find the threshold intensity where percentageCorrect is half way between the asymtotes at either end (center of the S-shaped curve)

I understand this to be a function maximization problem that can be solved by the Nelder Meade Simplex algorithm.

I am trying to solve my problem using the Nelder Meade Simplex algorithm in mathdotnet and its IObjectiveFunction parameter.

However, I am having trouble understanding the API of the NedlerMeadeSimplex class FindMinimum method and the IObjectiveFunction EvaluateAt method.

I am new to numerical analysis that is pre-requisite for this question.

Specific questions are:

  • For the NedlerMeadeSimplex class FindMinimum method what are the initialGuess and initialPertubation parameters?
  • For the IObjectiveFunction EvaluateAt method, what is the point parameter? I vaguely understand that the point parameter is a datum in the dataset being minimized
  • How can I map my data set to this API and solve my problem?

Thanks for any guidance on this.


Solution

  • The initial guess is a guess at the model parameters.

    I've always used the forms that don't require an entry of the initialPertubation parameter, so I can't help you there.

    The objective function is what your are trying to minimize. For example, for a least squares fit, it would calculate the sum of squared areas at the point given in the argument. Something like this:

    private double SumSqError(Vector<double> v)
    {
        double err = 0;
        for (int i = 0; i < 100; i++)
        {
            double y_val = v[0] + v[1] * Math.Exp(v[2] * x[i]);
            err += Math.Pow(y_val - y[i], 2);
        }
        return err;
    }
    

    You don't have to supply the point. The algorithm does that over and over while searching for the minimum. Note that the subroutine as access to the vector x.

    Here is the code for a test program fitting a function to random data:

    private void btnMinFit_Click(object sender, EventArgs e)
    {
        Random RanGen = new Random();
        x = new double[100];
        y = new double[100];
    
        // fit exponential expression with three parameters
        double a = 5.0;
        double b = 0.5;
        double c = 0.05;
        // create data set
        for (int i = 0; i < 100; i++) x[i] = 10 + Convert.ToDouble(i) * 90.0 / 99.0; // values span 10 to 100
        for (int i = 0; i < 100; i++)
        {
            double y_val = a + b * Math.Exp(c * x[i]);
            y[i] = y_val + 0.1 * RanGen.NextDouble() * y_val;  // add error term scaled to y-value
        }
    
        // var fphv = new Func<double, double, double, double>((x, A, B) => A * x + B * x + A * B * x * x); extraneous test
    
    
        var f1 = new Func<Vector<double>, double>(x => LogEval(x));
        var obj = ObjectiveFunction.Value(f1);
        var solver = new NelderMeadSimplex(1e-5, maximumIterations: 10000);
        var initialGuess = new DenseVector(new[] { 3.0, 6.0, 0.6 });
    
        var result = solver.FindMinimum(obj, initialGuess);
    
    
        Console.WriteLine(result.MinimizingPoint.ToString());
    
    }