Browse Source

Added tests and copyright notice

v3
Erik Ovegard 11 years ago
committed by Erik Ovegard
parent
commit
8478b1c883
  1. 66
      src/Numerics/Optimization/NelderMeadSimplex.cs
  2. 107
      src/UnitTests/OptimizationTests/NelderMeadSimplexTests.cs
  3. 1
      src/UnitTests/UnitTests.csproj

66
src/Numerics/Optimization/NelderMeadSimplex.cs

@ -1,4 +1,36 @@
using MathNet.Numerics.LinearAlgebra; // <copyright file="NelderMeadSimplex.cs" company="Math.NET">
// Math.NET Numerics, part of the Math.NET Project
// http://numerics.mathdotnet.com
// http://github.com/mathnet/mathnet-numerics
// http://mathnetnumerics.codeplex.com
//
// Copyright (c) 2009-2015 Math.NET
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
// </copyright>
// Converted from code relased with a MIT liscense available at https://code.google.com/p/nelder-mead-simplex/
using MathNet.Numerics.LinearAlgebra;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@ -10,17 +42,31 @@ namespace MathNet.Numerics.Optimization
{ {
private static readonly double JITTER = 1e-10d; // a small value used to protect against floating point noise private static readonly double JITTER = 1e-10d; // a small value used to protect against floating point noise
public static MinimizationResult Regress(IObjectiveFunction objectiveFunction, Vector<double> initialGuess, public double ConvergenceTolerance { get; set; }
double convergenceTolerance, int maxEvaluations) public int MaximumIterations { get; set; }
public NelderMeadSimplex(double convergenceTolerance, int maximumIterations)
{ {
SimplexConstant[] simplexConstants = SimplexConstant.CreateFromVector(initialGuess); ConvergenceTolerance = convergenceTolerance;
MaximumIterations = maximumIterations;
}
/// <summary>
/// Finds the minimum of the objective function
/// </summary>
/// <param name="objectiveFunction">The objective function, no gradient or hessian needed</param>
/// <param name="initialGuess">The intial guess</param>
/// <returns>The minimum point</returns>
public MinimizationResult FindMinimum(IObjectiveFunction objectiveFunction, Vector<double> initialGuess)
{
// confirm that we are in a position to commence // confirm that we are in a position to commence
if (objectiveFunction == null) if (objectiveFunction == null)
throw new InvalidOperationException("ObjectiveFunction must be set to a valid ObjectiveFunctionDelegate"); throw new ArgumentNullException("objectiveFunction","ObjectiveFunction must be set to a valid ObjectiveFunctionDelegate");
if (simplexConstants == null) if (initialGuess == null)
throw new InvalidOperationException("SimplexConstants must be initialized"); throw new ArgumentNullException("initialGuess", "initialGuess must be initialized");
SimplexConstant[] simplexConstants = SimplexConstant.CreateFromVector(initialGuess);
// create the initial simplex // create the initial simplex
int numDimensions = simplexConstants.Length; int numDimensions = simplexConstants.Length;
@ -40,7 +86,7 @@ namespace MathNet.Numerics.Optimization
errorProfile = _evaluateSimplex(errorValues); errorProfile = _evaluateSimplex(errorValues);
// see if the range in point heights is small enough to exit // see if the range in point heights is small enough to exit
if (_hasConverged(convergenceTolerance, errorProfile, errorValues)) if (_hasConverged(ConvergenceTolerance, errorProfile, errorValues))
{ {
exitCondition = MinimizationResult.ExitCondition.Converged; exitCondition = MinimizationResult.ExitCondition.Converged;
break; break;
@ -72,13 +118,13 @@ namespace MathNet.Numerics.Optimization
} }
} }
// check to see if we have exceeded our alloted number of evaluations // check to see if we have exceeded our alloted number of evaluations
if (evaluationCount >= maxEvaluations) if (evaluationCount >= MaximumIterations)
{ {
exitCondition = MinimizationResult.ExitCondition.LackOfProgress; exitCondition = MinimizationResult.ExitCondition.LackOfProgress;
break; break;
} }
} }
var regressionResult = new MinimizationResult(null, evaluationCount, exitCondition); var regressionResult = new MinimizationResult(objectiveFunction, evaluationCount, exitCondition);
return regressionResult; return regressionResult;
} }

107
src/UnitTests/OptimizationTests/NelderMeadSimplexTests.cs

@ -0,0 +1,107 @@
// <copyright file="NelderMeadSimplexTests.cs" company="Math.NET">
// Math.NET Numerics, part of the Math.NET Project
// http://numerics.mathdotnet.com
// http://github.com/mathnet/mathnet-numerics
// http://mathnetnumerics.codeplex.com
//
// Copyright (c) 2009-2015 Math.NET
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
// </copyright>
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MathNet.Numerics.Optimization;
using MathNet.Numerics.LinearAlgebra.Double;
namespace MathNet.Numerics.UnitTests.OptimizationTests
{
[TestFixture]
public class NelderMeadSimplexTests
{
/// <summary>
/// Test that finds the constants of a parable, function adds noise and return the mean square error
/// Copied from the test in https://code.google.com/p/nelder-mead-simplex/
/// </summary>
[Test]
public void FindParableConstantsThatMinimizesErrors()
{
var nms = new NelderMeadSimplex(1e-6, 1000);
double a = 5;
double b = 10;
IObjectiveFunction objFun = ObjectiveFunction.Value((constants)=>
{
double ssq = 0;
System.Random r = new System.Random();
for (double x = -10; x < 10; x += .1)
{
double yTrue = a * x * x + b * x + r.NextDouble();
double yRegress = constants[0] * x * x + constants[1] * x;
ssq += Math.Pow((yTrue - yRegress), 2);
}
return ssq;
});
var initialGuess = new DenseVector(2);
initialGuess[0] = 3;
initialGuess[1] = 5;
var result = nms.FindMinimum(objFun, initialGuess);
Assert.NotNull(result);
Assert.NotNull(result.MinimizingPoint);
Assert.NotNull(result.FunctionInfoAtMinimum);
Assert.That(Math.Abs(result.MinimizingPoint[0] - a), Is.LessThan(1e-2));
Assert.That(Math.Abs(result.MinimizingPoint[1] - b), Is.LessThan(1e-2));
}
[Test]
public void NMS_FindMinimum_Rosenbrock_Easy()
{
var obj = ObjectiveFunction.Value(RosenbrockFunction.Value);
var solver = new NelderMeadSimplex(1e-5, maximumIterations: 1000);
var initialGuess = new DenseVector(new[] { 1.2, 1.2 });
var result = solver.FindMinimum(obj, initialGuess);
Assert.That(Math.Abs(result.MinimizingPoint[0] - 1.0), Is.LessThan(1e-3));
Assert.That(Math.Abs(result.MinimizingPoint[1] - 1.0), Is.LessThan(1e-3));
}
[Test]
public void NMS_FindMinimum_Rosenbrock_Hard()
{
var obj = ObjectiveFunction.Value(RosenbrockFunction.Value);
var solver = new NelderMeadSimplex(1e-5, maximumIterations: 1000);
var initialGuess = new DenseVector(new[] { -1.2, 1.0 });
var result = solver.FindMinimum(obj,initialGuess);
Assert.That(Math.Abs(result.MinimizingPoint[0] - 1.0), Is.LessThan(1e-3));
Assert.That(Math.Abs(result.MinimizingPoint[1] - 1.0), Is.LessThan(1e-3));
}
}
}

1
src/UnitTests/UnitTests.csproj

@ -368,6 +368,7 @@
<Compile Include="Random\RandomSerializationTests.cs" /> <Compile Include="Random\RandomSerializationTests.cs" />
<Compile Include="OptimizationTests\RosenbrockFunction.cs" /> <Compile Include="OptimizationTests\RosenbrockFunction.cs" />
<Compile Include="OptimizationTests\TestNewtonMinimizer.cs" /> <Compile Include="OptimizationTests\TestNewtonMinimizer.cs" />
<Compile Include="OptimizationTests\NelderMeadSimplexTests.cs" />
<Compile Include="OptimizationTests\TestGoldenSectionMinimizer.cs" /> <Compile Include="OptimizationTests\TestGoldenSectionMinimizer.cs" />
<Compile Include="Random\SystemRandomSourceTests.cs" /> <Compile Include="Random\SystemRandomSourceTests.cs" />
<Compile Include="OptimizationTests\BfgsTest.cs" /> <Compile Include="OptimizationTests\BfgsTest.cs" />

Loading…
Cancel
Save