From d633f8e608e38587e4abbfff74cbb5e62cecdbd4 Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Sat, 15 Jul 2017 17:06:28 +0200 Subject: [PATCH] Optimization: NelderMeadSimplex: allow static usage --- .../Optimization/NelderMeadSimplex.cs | 98 +++++++++---------- .../NelderMeadSimplexTests.cs | 3 +- 2 files changed, 48 insertions(+), 53 deletions(-) diff --git a/src/Numerics/Optimization/NelderMeadSimplex.cs b/src/Numerics/Optimization/NelderMeadSimplex.cs index c9dbb636..68551786 100644 --- a/src/Numerics/Optimization/NelderMeadSimplex.cs +++ b/src/Numerics/Optimization/NelderMeadSimplex.cs @@ -43,7 +43,7 @@ namespace MathNet.Numerics.Optimization /// public sealed class NelderMeadSimplex { - private static readonly double JITTER = 1e-10d; // a small value used to protect against floating point noise + static readonly double JITTER = 1e-10d; // a small value used to protect against floating point noise public double ConvergenceTolerance { get; set; } public int MaximumIterations { get; set; } @@ -63,13 +63,38 @@ namespace MathNet.Numerics.Optimization /// The intial guess /// The minimum point public MinimizationResult FindMinimum(IObjectiveFunction objectiveFunction, Vector initialGuess) + { + return FindMinimum(objectiveFunction, initialGuess, ConvergenceTolerance, MaximumIterations); + } + + /// + /// Finds the minimum of the objective function with an intial pertubation + /// + /// The objective function, no gradient or hessian needed + /// The intial guess + /// The inital pertubation + /// The minimum point + public MinimizationResult FindMinimum(IObjectiveFunction objectiveFunction, Vector initialGuess, Vector initalPertubation) + { + return FindMinimum(objectiveFunction, initialGuess, initalPertubation, ConvergenceTolerance, MaximumIterations); + } + + /// + /// Finds the minimum of the objective function without an intial pertubation, the default values used + /// by fminsearch() in Matlab are used instead + /// http://se.mathworks.com/help/matlab/math/optimizing-nonlinear-functions.html#bsgpq6p-11 + /// + /// The objective function, no gradient or hessian needed + /// The intial guess + /// The minimum point + public static MinimizationResult FindMinimum(IObjectiveFunction objectiveFunction, Vector initialGuess, double convergenceTolerance, int maximumIterations) { var initalPertubation = new LinearAlgebra.Double.DenseVector(initialGuess.Count); for (int i = 0; i < initialGuess.Count; i++) { initalPertubation[i] = initialGuess[i] == 0.0 ? 0.00025 : initialGuess[i] * 0.05; } - return FindMinimum(objectiveFunction, initialGuess, initalPertubation); + return FindMinimum(objectiveFunction, initialGuess, initalPertubation, convergenceTolerance, maximumIterations); } /// @@ -79,7 +104,7 @@ namespace MathNet.Numerics.Optimization /// The intial guess /// The inital pertubation /// The minimum point - public MinimizationResult FindMinimum(IObjectiveFunction objectiveFunction, Vector initialGuess, Vector initalPertubation) + public static MinimizationResult FindMinimum(IObjectiveFunction objectiveFunction, Vector initialGuess, Vector initalPertubation, double convergenceTolerance, int maximumIterations) { // confirm that we are in a position to commence if (objectiveFunction == null) @@ -111,7 +136,7 @@ namespace MathNet.Numerics.Optimization errorProfile = EvaluateSimplex(errorValues); // see if the range in point heights is small enough to exit - if (HasConverged(ConvergenceTolerance, errorProfile, errorValues)) + if (HasConverged(convergenceTolerance, errorProfile, errorValues)) { exitCondition = ExitCondition.Converged; break; @@ -143,9 +168,9 @@ namespace MathNet.Numerics.Optimization } } // check to see if we have exceeded our alloted number of evaluations - if (evaluationCount >= MaximumIterations) + if (evaluationCount >= maximumIterations) { - throw new MaximumIterationsException(String.Format("Maximum iterations ({0}) reached.", MaximumIterations)); + throw new MaximumIterationsException(String.Format("Maximum iterations ({0}) reached.", maximumIterations)); } } var regressionResult = new MinimizationResult(objectiveFunction, evaluationCount, exitCondition); @@ -159,7 +184,7 @@ namespace MathNet.Numerics.Optimization /// /// /// - private static double[] InitializeErrorValues(Vector[] vertices, IObjectiveFunction objectiveFunction) + static double[] InitializeErrorValues(Vector[] vertices, IObjectiveFunction objectiveFunction) { double[] errorValues = new double[vertices.Length]; for (int i = 0; i < vertices.Length; i++) @@ -178,7 +203,7 @@ namespace MathNet.Numerics.Optimization /// /// /// - private static bool HasConverged(double convergenceTolerance, ErrorProfile errorProfile, double[] errorValues) + static bool HasConverged(double convergenceTolerance, ErrorProfile errorProfile, double[] errorValues) { double range = 2 * Math.Abs(errorValues[errorProfile.HighestIndex] - errorValues[errorProfile.LowestIndex]) / (Math.Abs(errorValues[errorProfile.HighestIndex]) + Math.Abs(errorValues[errorProfile.LowestIndex]) + JITTER); @@ -198,7 +223,7 @@ namespace MathNet.Numerics.Optimization /// /// /// - private static ErrorProfile EvaluateSimplex(double[] errorValues) + static ErrorProfile EvaluateSimplex(double[] errorValues) { ErrorProfile errorProfile = new ErrorProfile(); if (errorValues[0] > errorValues[1]) @@ -239,7 +264,7 @@ namespace MathNet.Numerics.Optimization /// /// /// - private static Vector[] InitializeVertices(SimplexConstant[] simplexConstants) + static Vector[] InitializeVertices(SimplexConstant[] simplexConstants) { int numDimensions = simplexConstants.Length; Vector[] vertices = new Vector[numDimensions + 1]; @@ -273,7 +298,7 @@ namespace MathNet.Numerics.Optimization /// /// /// - private static double TryToScaleSimplex(double scaleFactor, ref ErrorProfile errorProfile, Vector[] vertices, + static double TryToScaleSimplex(double scaleFactor, ref ErrorProfile errorProfile, Vector[] vertices, double[] errorValues, IObjectiveFunction objectiveFunction) { // find the centroid through which we will reflect @@ -306,7 +331,7 @@ namespace MathNet.Numerics.Optimization /// /// /// - private static void ShrinkSimplex(ErrorProfile errorProfile, Vector[] vertices, double[] errorValues, + static void ShrinkSimplex(ErrorProfile errorProfile, Vector[] vertices, double[] errorValues, IObjectiveFunction objectiveFunction) { Vector lowestVertex = vertices[errorProfile.LowestIndex]; @@ -327,7 +352,7 @@ namespace MathNet.Numerics.Optimization /// /// /// - private static Vector ComputeCentroid(Vector[] vertices, ErrorProfile errorProfile) + static Vector ComputeCentroid(Vector[] vertices, ErrorProfile errorProfile) { int numVertices = vertices.Length; // find the centroid of all points except the worst one @@ -342,32 +367,21 @@ namespace MathNet.Numerics.Optimization return centroid.Multiply(1.0d / (numVertices - 1)); } - private sealed class SimplexConstant + sealed class SimplexConstant { - private double _value; - private double _initialPerturbation; - public SimplexConstant(double value, double initialPerturbation) { - _value = value; - _initialPerturbation = initialPerturbation; + Value = value; + InitialPerturbation = initialPerturbation; } /// /// The value of the constant /// - public double Value - { - get { return _value; } - set { _value = value; } - } + public double Value { get; } // The size of the initial perturbation - public double InitialPerturbation - { - get { return _initialPerturbation; } - set { _initialPerturbation = value; } - } + public double InitialPerturbation { get; } public static SimplexConstant[] CreateSimplexConstantsFromVectors(Vector initialGuess, Vector initialPertubation) { @@ -380,29 +394,11 @@ namespace MathNet.Numerics.Optimization } } - private sealed class ErrorProfile + sealed class ErrorProfile { - private int _highestIndex; - private int _nextHighestIndex; - private int _lowestIndex; - - public int HighestIndex - { - get { return _highestIndex; } - set { _highestIndex = value; } - } - - public int NextHighestIndex - { - get { return _nextHighestIndex; } - set { _nextHighestIndex = value; } - } - - public int LowestIndex - { - get { return _lowestIndex; } - set { _lowestIndex = value; } - } + public int HighestIndex { get; set; } + public int NextHighestIndex { get; set; } + public int LowestIndex { get; set; } } } } diff --git a/src/UnitTests/OptimizationTests/NelderMeadSimplexTests.cs b/src/UnitTests/OptimizationTests/NelderMeadSimplexTests.cs index 3d0f0c75..54fc124a 100644 --- a/src/UnitTests/OptimizationTests/NelderMeadSimplexTests.cs +++ b/src/UnitTests/OptimizationTests/NelderMeadSimplexTests.cs @@ -111,9 +111,8 @@ namespace MathNet.Numerics.UnitTests.OptimizationTests public void Mgh_Tests(TestFunctions.TestCase test_case) { var obj = new MghObjectiveFunction(test_case.Function, true, true); - var solver = new NelderMeadSimplex(1e-8, 1000); - var result = solver.FindMinimum(obj, test_case.InitialGuess); + var result = NelderMeadSimplex.FindMinimum(obj, test_case.InitialGuess, 1e-8, 1000); if (test_case.MinimizingPoint != null) {