Optimization In One Dimension in C# QuickStart Sample

Illustrates the use of the Brent and Golden Section optimizer classes in the Numerics.NET.Optimization namespace for one-dimensional optimization in C#.

This sample is also available in: Visual Basic, F#, IronPython.

Overview

This QuickStart sample demonstrates how to find the minimum or maximum of a function in one dimension using Numerics.NET’s optimization capabilities.

The sample shows two different optimization algorithms:

  • Brent’s algorithm - A sophisticated and efficient method that combines golden section search with parabolic interpolation. It is the recommended method for most one-dimensional optimization problems. The sample demonstrates how to:
    • Create and configure a BrentOptimizer
    • Find an interval containing an extremum using FindBracket
    • Locate the precise minimum or maximum
    • Access results and error estimates
  • Golden Section Search - A simpler but slower algorithm based on the golden ratio. The sample shows how to use the GoldenSectionOptimizer class to find extrema.

Both methods are demonstrated on two test functions:

  1. A cubic polynomial f(x) = x� - 2x - 5
  2. An exponential function f(x) = 1/exp(x� - 0.7x + 0.2)

The sample illustrates proper error handling, accessing optimization results, and comparing estimated versus actual errors. It also shows how to use lambda expressions for defining objective functions in C# 3.0 and later.

The code

using System;
// The optimization classes resides in the
// Numerics.NET.EquationSolvers namespace.
using Numerics.NET.Optimization;
// Function delegates reside in the Numerics.NET
// namespace.
using Numerics.NET;

// Illustrates the use of the Brent and Golden Section optimizers
// in the Numerics.NET.Optimization namespace of Numerics.NET.

// The license is verified at runtime. We're using
// a 30 day trial key here. For more information, see
//     https://numerics.net/trial-key
Numerics.NET.License.Verify("your-trial-key-here");

// Several algorithms exist for optimizing functions
// in one variable. The most common one is
// Brent's algorithm.

//
// Brent's algorithm
//

// Now let's create the BrentOptimizer object.
BrentOptimizer optimizer = new BrentOptimizer();

// Set the objective function:
optimizer.ObjectiveFunction = x => x * x * x - 2 * x - 5;

// Optimizers can find either a minimum or a maximum.
// Which of the two is specified by the ExtremumType
// property
optimizer.ExtremumType = ExtremumType.Minimum;

// The first phase is to find an interval that contains
// a local minimum. This is done by the FindBracket method.
optimizer.FindBracket(0, 3);
// You can verify that an interval was found from the
// IsBracketValid property:
if (!optimizer.IsBracketValid)
    throw new Exception("An interval containing a minimum was not found.");

// Finally, we can run the optimizer by calling the FindExtremum method:
optimizer.FindExtremum();

Console.WriteLine("Function 1: x^3 - 2x - 5");
// The Status property indicates
// the result of running the algorithm.
Console.WriteLine($"  Status: {optimizer.Status}");
// The result is available through the
// Result property.
Console.WriteLine($"  Minimum: {optimizer.Result}");
double exactResult = Math.Sqrt(2/3.0);
double result = optimizer.Extremum;
Console.WriteLine($"  Exact minimum: {exactResult}");

// You can find out the estimated error of the result
// through the EstimatedError property:
Console.WriteLine($"  Estimated error: {optimizer.EstimatedError}");
Console.WriteLine($"  Actual error: {Math.Abs(result - exactResult)}");
Console.WriteLine($"  # iterations: {optimizer.IterationsNeeded}");

Console.WriteLine("Function 2: 1/Exp(x*x - 0.7*x +0.2)");
// You can also perform these calculations more directly
// using the FindMinimum or FindMaximum methods. This implicitly
// calls the FindBracket method.
result = optimizer.FindMaximum(TestFunction2, 0);
Console.WriteLine($"  Maximum: {result}");
Console.WriteLine($"  Actual maximum: {0.35}");
Console.WriteLine($"  Estimated error: {optimizer.EstimatedError}");
Console.WriteLine($"  Actual error: {result - 0.35}");
Console.WriteLine($"  # iterations: {optimizer.IterationsNeeded}");

//
// Golden section search
//

// A slower but simpler algorithm for finding an extremum
// is the golden section search. It is implemented by the
// GoldenSectionMinimizer class:
GoldenSectionOptimizer optimizer2 = new GoldenSectionOptimizer();

Console.WriteLine("Using Golden Section optimizer:");
result = optimizer2.FindMaximum(TestFunction2, 0);
Console.WriteLine($"  Maximum: {result}");
Console.WriteLine($"  Actual maximum: {0.35}");
Console.WriteLine($"  Estimated error: {optimizer2.EstimatedError}");
Console.WriteLine($"  Actual error: {result - 0.35}");
Console.WriteLine($"  # iterations: {optimizer2.IterationsNeeded}");

Console.Write("Press Enter key to exit...");
Console.ReadLine();

// Minimum at x = Sqrt(2/3) = 0.816496580927726
static double TestFunction1(double x)
{
    return x * x * x - 2 * x - 5;
}

// Maximum at x = 0.35
static double TestFunction2(double x)
{
    return 1 / Math.Exp(x * x - 0.7 * x + 0.2);
}