forked from tsai/mathnet-numerics
committed by
Christoph Ruegg
4 changed files with 458 additions and 72 deletions
@ -0,0 +1,179 @@ |
|||||
|
// <copyright file="BfgsMinimizer.cs" company="Math.NET">
|
||||
|
// Math.NET Numerics, part of the Math.NET Project
|
||||
|
// http://numerics.mathdotnet.com
|
||||
|
// http://github.com/mathnet/mathnet-numerics
|
||||
|
//
|
||||
|
// Copyright (c) 2009-2017 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 System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using MathNet.Numerics.LinearAlgebra; |
||||
|
using MathNet.Numerics.Optimization.LineSearch; |
||||
|
|
||||
|
namespace MathNet.Numerics.Optimization |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Limited Memory version of Broyden–Fletcher–Goldfarb–Shanno (BFGS) algorithm
|
||||
|
/// </summary>
|
||||
|
public class LimitedMemoryBfgsMinimizer : MinimizerBase, IUnconstrainedMinimizer |
||||
|
{ |
||||
|
public int Memory { get; set; } |
||||
|
|
||||
|
/// <inheritdoc />
|
||||
|
/// <summary>
|
||||
|
/// Creates L-BFGS minimizer
|
||||
|
/// </summary>
|
||||
|
/// <param name="memory">Numbers of gradients and steps to store.</param>
|
||||
|
public LimitedMemoryBfgsMinimizer(double gradientTolerance, double parameterTolerance, double functionProgressTolerance, int memory, int maximumIterations=1000) : base(gradientTolerance, parameterTolerance, functionProgressTolerance, maximumIterations) |
||||
|
{ |
||||
|
Memory = memory; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Find the minimum of the objective function given lower and upper bounds
|
||||
|
/// </summary>
|
||||
|
/// <param name="objective">The objective function, must support a gradient</param>
|
||||
|
/// <param name="initialGuess">The initial guess</param>
|
||||
|
/// <returns>The MinimizationResult which contains the minimum and the ExitCondition</returns>
|
||||
|
public MinimizationResult FindMinimum(IObjectiveFunction objective, Vector<double> initialGuess) |
||||
|
{ |
||||
|
if (!objective.IsGradientSupported) |
||||
|
throw new IncompatibleObjectiveException("Gradient not supported in objective function, but required for L-BFGS minimization."); |
||||
|
|
||||
|
objective.EvaluateAt(initialGuess); |
||||
|
ValidateGradientAndObjective(objective); |
||||
|
|
||||
|
// Check that we're not already done
|
||||
|
ExitCondition currentExitCondition = ExitCriteriaSatisfied(objective, null, 0); |
||||
|
if (currentExitCondition != ExitCondition.None) |
||||
|
return new MinimizationResult(objective, 0, currentExitCondition); |
||||
|
|
||||
|
// Set up line search algorithm
|
||||
|
var lineSearcher = new WeakWolfeLineSearch(1e-4, 0.9, Math.Max(ParameterTolerance, 1e-10), 1000); |
||||
|
|
||||
|
// First step
|
||||
|
|
||||
|
var lineSearchDirection = -objective.Gradient; |
||||
|
var stepSize = 100 * GradientTolerance / (lineSearchDirection * lineSearchDirection); |
||||
|
|
||||
|
var previousPoint = objective; |
||||
|
|
||||
|
LineSearchResult lineSearchResult; |
||||
|
try |
||||
|
{ |
||||
|
lineSearchResult = lineSearcher.FindConformingStep(objective, lineSearchDirection, stepSize); |
||||
|
} |
||||
|
catch (OptimizationException e) |
||||
|
{ |
||||
|
throw new InnerOptimizationException("Line search failed.", e); |
||||
|
} |
||||
|
catch (ArgumentException e) |
||||
|
{ |
||||
|
throw new InnerOptimizationException("Line search failed.", e); |
||||
|
} |
||||
|
|
||||
|
var candidate = lineSearchResult.FunctionInfoAtMinimum; |
||||
|
ValidateGradientAndObjective(candidate); |
||||
|
|
||||
|
var gradient = candidate.Gradient; |
||||
|
var step = candidate.Point - initialGuess; |
||||
|
var yk = candidate.Gradient - previousPoint.Gradient; |
||||
|
var ykhistory = new List<Vector<double>>() {yk}; |
||||
|
var skhistory = new List<Vector<double>>() {step}; |
||||
|
var rhokhistory = new List<double>() {1.0/yk.DotProduct(step)}; |
||||
|
|
||||
|
// Subsequent steps
|
||||
|
int iterations = 1; |
||||
|
int totalLineSearchSteps = lineSearchResult.Iterations; |
||||
|
int iterationsWithNontrivialLineSearch = lineSearchResult.Iterations > 0 ? 0 : 1; |
||||
|
previousPoint = candidate; |
||||
|
while (iterations++ < MaximumIterations && previousPoint.Gradient.Norm(2) >= GradientTolerance) |
||||
|
{ |
||||
|
lineSearchDirection = -ApplyLbfgsUpdate(previousPoint, ykhistory, skhistory, rhokhistory); |
||||
|
var directionalDerivative = previousPoint.Gradient.DotProduct(lineSearchDirection); |
||||
|
if (directionalDerivative > 0) |
||||
|
throw new InnerOptimizationException("Direction is not a descent direction."); |
||||
|
try |
||||
|
{ |
||||
|
lineSearchResult = lineSearcher.FindConformingStep(previousPoint, lineSearchDirection, 1.0); |
||||
|
} |
||||
|
catch (OptimizationException e) |
||||
|
{ |
||||
|
throw new InnerOptimizationException("Line search failed.", e); |
||||
|
} |
||||
|
catch (ArgumentException e) |
||||
|
{ |
||||
|
throw new InnerOptimizationException("Line search failed.", e); |
||||
|
} |
||||
|
iterationsWithNontrivialLineSearch += lineSearchResult.Iterations > 0 ? 1 : 0; |
||||
|
totalLineSearchSteps += lineSearchResult.Iterations; |
||||
|
|
||||
|
candidate = lineSearchResult.FunctionInfoAtMinimum; |
||||
|
currentExitCondition = ExitCriteriaSatisfied(candidate, previousPoint, iterations); |
||||
|
if (currentExitCondition != ExitCondition.None) |
||||
|
break; |
||||
|
step = candidate.Point - previousPoint.Point; |
||||
|
yk = candidate.Gradient - previousPoint.Gradient; |
||||
|
ykhistory.Add(yk); |
||||
|
skhistory.Add(step); |
||||
|
rhokhistory.Add(1.0/yk.DotProduct(step)); |
||||
|
previousPoint = candidate; |
||||
|
if (ykhistory.Count > Memory) |
||||
|
{ |
||||
|
ykhistory.RemoveAt(0); |
||||
|
skhistory.RemoveAt(0); |
||||
|
rhokhistory.RemoveAt(0); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (iterations == MaximumIterations && currentExitCondition == ExitCondition.None) |
||||
|
throw new MaximumIterationsException(String.Format("Maximum iterations ({0}) reached.", MaximumIterations)); |
||||
|
|
||||
|
return new MinimizationWithLineSearchResult(candidate, iterations, ExitCondition.AbsoluteGradient, totalLineSearchSteps, iterationsWithNontrivialLineSearch); |
||||
|
} |
||||
|
|
||||
|
private Vector<double> ApplyLbfgsUpdate(IObjectiveFunction previousPoint, List<Vector<double>> ykhistory, List<Vector<double>> skhistory, List<double> rhokhistory) |
||||
|
{ |
||||
|
var q = previousPoint.Gradient.Clone(); |
||||
|
var alphas = new Stack<double>(); |
||||
|
for (int k = ykhistory.Count - 1; k >= 0; k--) |
||||
|
{ |
||||
|
var alpha = rhokhistory[k]*q.DotProduct(skhistory[k]); |
||||
|
alphas.Push(alpha); |
||||
|
q -= alpha*ykhistory[k]; |
||||
|
} |
||||
|
var yk = ykhistory.Last(); |
||||
|
var sk = skhistory.Last(); |
||||
|
q *= yk.DotProduct(sk)/yk.DotProduct(yk); |
||||
|
for (int k = 0; k < ykhistory.Count; k++) |
||||
|
{ |
||||
|
var beta = rhokhistory[k]*ykhistory[k].DotProduct(q); |
||||
|
q += skhistory[k]*(alphas.Pop() - beta); |
||||
|
} |
||||
|
return q; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,117 @@ |
|||||
|
// <copyright file="BfgsMinimizerBase.cs" company="Math.NET">
|
||||
|
// Math.NET Numerics, part of the Math.NET Project
|
||||
|
// http://numerics.mathdotnet.com
|
||||
|
// http://github.com/mathnet/mathnet-numerics
|
||||
|
//
|
||||
|
// Copyright (c) 2009-2017 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 MathNet.Numerics.LinearAlgebra; |
||||
|
using MathNet.Numerics.LinearAlgebra.Double; |
||||
|
using MathNet.Numerics.Optimization.LineSearch; |
||||
|
using System; |
||||
|
|
||||
|
namespace MathNet.Numerics.Optimization |
||||
|
{ |
||||
|
public abstract class MinimizerBase |
||||
|
{ |
||||
|
public double GradientTolerance { get; set; } |
||||
|
public double ParameterTolerance { get; set; } |
||||
|
public double FunctionProgressTolerance { get; set; } |
||||
|
public int MaximumIterations { get; set; } |
||||
|
|
||||
|
protected const double VerySmall = 1e-15; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Creates a base class for minimization
|
||||
|
/// </summary>
|
||||
|
/// <param name="gradientTolerance">The gradient tolerance</param>
|
||||
|
/// <param name="parameterTolerance">The parameter tolerance</param>
|
||||
|
/// <param name="functionProgressTolerance">The funciton progress tolerance</param>
|
||||
|
/// <param name="maximumIterations">The maximum number of iterations</param>
|
||||
|
protected MinimizerBase(double gradientTolerance, double parameterTolerance, double functionProgressTolerance, int maximumIterations) |
||||
|
{ |
||||
|
GradientTolerance = gradientTolerance; |
||||
|
ParameterTolerance = parameterTolerance; |
||||
|
FunctionProgressTolerance = functionProgressTolerance; |
||||
|
MaximumIterations = maximumIterations; |
||||
|
} |
||||
|
|
||||
|
protected ExitCondition ExitCriteriaSatisfied(IObjectiveFunctionEvaluation candidatePoint, IObjectiveFunctionEvaluation lastPoint, int iterations) |
||||
|
{ |
||||
|
Vector<double> relGrad = new DenseVector(candidatePoint.Point.Count); |
||||
|
double relativeGradient = 0.0; |
||||
|
double normalizer = Math.Max(Math.Abs(candidatePoint.Value), 1.0); |
||||
|
for (int ii = 0; ii < relGrad.Count; ++ii) |
||||
|
{ |
||||
|
double projectedGradient = GetProjectedGradient(candidatePoint, ii); |
||||
|
|
||||
|
double tmp = projectedGradient * |
||||
|
Math.Max(Math.Abs(candidatePoint.Point[ii]), 1.0) / normalizer; |
||||
|
relativeGradient = Math.Max(relativeGradient, Math.Abs(tmp)); |
||||
|
} |
||||
|
if (relativeGradient < GradientTolerance) |
||||
|
{ |
||||
|
return ExitCondition.RelativeGradient; |
||||
|
} |
||||
|
|
||||
|
if (lastPoint != null) |
||||
|
{ |
||||
|
double mostProgress = 0.0; |
||||
|
for (int ii = 0; ii < candidatePoint.Point.Count; ++ii) |
||||
|
{ |
||||
|
var tmp = Math.Abs(candidatePoint.Point[ii] - lastPoint.Point[ii]) / |
||||
|
Math.Max(Math.Abs(lastPoint.Point[ii]), 1.0); |
||||
|
mostProgress = Math.Max(mostProgress, tmp); |
||||
|
} |
||||
|
if (mostProgress < ParameterTolerance) |
||||
|
{ |
||||
|
return ExitCondition.LackOfProgress; |
||||
|
} |
||||
|
|
||||
|
double functionChange = candidatePoint.Value - lastPoint.Value; |
||||
|
if (iterations > 500 && functionChange < 0 && Math.Abs(functionChange) < FunctionProgressTolerance) |
||||
|
return ExitCondition.LackOfProgress; |
||||
|
} |
||||
|
|
||||
|
return ExitCondition.None; |
||||
|
} |
||||
|
|
||||
|
protected virtual double GetProjectedGradient(IObjectiveFunctionEvaluation candidatePoint, int ii) |
||||
|
{ |
||||
|
return candidatePoint.Gradient[ii]; |
||||
|
} |
||||
|
|
||||
|
protected void ValidateGradientAndObjective(IObjectiveFunctionEvaluation eval) |
||||
|
{ |
||||
|
foreach (var x in eval.Gradient) |
||||
|
{ |
||||
|
if (Double.IsNaN(x) || Double.IsInfinity(x)) |
||||
|
throw new EvaluationException("Non-finite gradient returned.", eval); |
||||
|
} |
||||
|
if (Double.IsNaN(eval.Value) || Double.IsInfinity(eval.Value)) |
||||
|
throw new EvaluationException("Non-finite objective function returned.", eval); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,159 @@ |
|||||
|
// <copyright file="BfgsMinimizerTests.cs" company="Math.NET">
|
||||
|
// Math.NET Numerics, part of the Math.NET Project
|
||||
|
// http://numerics.mathdotnet.com
|
||||
|
// http://github.com/mathnet/mathnet-numerics
|
||||
|
//
|
||||
|
// Copyright (c) 2009-2017 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 System; |
||||
|
using System.Linq; |
||||
|
using MathNet.Numerics.LinearAlgebra.Double; |
||||
|
using MathNet.Numerics.Optimization; |
||||
|
using NUnit.Framework; |
||||
|
using MathNet.Numerics.UnitTests.OptimizationTests.TestFunctions; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Collections; |
||||
|
using NUnit.Framework.Interfaces; |
||||
|
|
||||
|
namespace MathNet.Numerics.UnitTests.OptimizationTests |
||||
|
{ |
||||
|
[TestFixture] |
||||
|
public class LBfgsMinimizerTests |
||||
|
{ |
||||
|
[Test] |
||||
|
public void FindMinimum_Rosenbrock_Easy() |
||||
|
{ |
||||
|
var obj = ObjectiveFunction.Gradient(RosenbrockFunction.Value, RosenbrockFunction.Gradient); |
||||
|
var solver = new LimitedMemoryBfgsMinimizer(1e-5, 1e-5, 1e-5, 5, 100); |
||||
|
var result = solver.FindMinimum(obj, new DenseVector(new[] { 1.2, 1.2 })); |
||||
|
|
||||
|
Assert.That(Math.Abs(result.MinimizingPoint[0] - RosenbrockFunction.Minimum[0]), Is.LessThan(1e-3)); |
||||
|
Assert.That(Math.Abs(result.MinimizingPoint[1] - RosenbrockFunction.Minimum[1]), Is.LessThan(1e-3)); |
||||
|
} |
||||
|
|
||||
|
[Test] |
||||
|
public void FindMinimum_Rosenbrock_Hard() |
||||
|
{ |
||||
|
var obj = ObjectiveFunction.Gradient(RosenbrockFunction.Value, RosenbrockFunction.Gradient); |
||||
|
var solver = new LimitedMemoryBfgsMinimizer(1e-5, 1e-5, 1e-5, 5, 100); |
||||
|
var result = solver.FindMinimum(obj, new DenseVector(new[] { -1.2, 1.0 })); |
||||
|
|
||||
|
Assert.That(Math.Abs(result.MinimizingPoint[0] - RosenbrockFunction.Minimum[0]), Is.LessThan(1e-3)); |
||||
|
Assert.That(Math.Abs(result.MinimizingPoint[1] - RosenbrockFunction.Minimum[1]), Is.LessThan(1e-3)); |
||||
|
} |
||||
|
|
||||
|
[Test] |
||||
|
public void FindMinimum_Rosenbrock_Overton() |
||||
|
{ |
||||
|
var obj = ObjectiveFunction.Gradient(RosenbrockFunction.Value, RosenbrockFunction.Gradient); |
||||
|
var solver = new LimitedMemoryBfgsMinimizer(1e-5, 1e-5, 1e-5, 5, 100); |
||||
|
var result = solver.FindMinimum(obj, new DenseVector(new[] { -0.9, -0.5 })); |
||||
|
|
||||
|
Assert.That(Math.Abs(result.MinimizingPoint[0] - RosenbrockFunction.Minimum[0]), Is.LessThan(1e-3)); |
||||
|
Assert.That(Math.Abs(result.MinimizingPoint[1] - RosenbrockFunction.Minimum[1]), Is.LessThan(1e-3)); |
||||
|
} |
||||
|
|
||||
|
[Test] |
||||
|
public void FindMinimum_BigRosenbrock_Easy() |
||||
|
{ |
||||
|
var obj = ObjectiveFunction.Gradient(BigRosenbrockFunction.Value, BigRosenbrockFunction.Gradient); |
||||
|
var solver = new LimitedMemoryBfgsMinimizer(1e-10, 1e-5, 1e-5, 5, 1000); |
||||
|
var result = solver.FindMinimum(obj, new DenseVector(new[] { 1.2 * 100.0, 1.2 * 100.0 })); |
||||
|
|
||||
|
Assert.That(Math.Abs(result.MinimizingPoint[0] - BigRosenbrockFunction.Minimum[0]), Is.LessThan(1e-3)); |
||||
|
Assert.That(Math.Abs(result.MinimizingPoint[1] - BigRosenbrockFunction.Minimum[1]), Is.LessThan(1e-3)); |
||||
|
} |
||||
|
|
||||
|
[Test] |
||||
|
public void FindMinimum_BigRosenbrock_Hard() |
||||
|
{ |
||||
|
var obj = ObjectiveFunction.Gradient(BigRosenbrockFunction.Value, BigRosenbrockFunction.Gradient); |
||||
|
var solver = new LimitedMemoryBfgsMinimizer(1e-5, 1e-5, 1e-5, 5, 1000); |
||||
|
var result = solver.FindMinimum(obj, new DenseVector(new[] { -1.2 * 100.0, 1.0 * 100.0 })); |
||||
|
|
||||
|
Assert.That(Math.Abs(result.MinimizingPoint[0] - BigRosenbrockFunction.Minimum[0]), Is.LessThan(1e-3)); |
||||
|
Assert.That(Math.Abs(result.MinimizingPoint[1] - BigRosenbrockFunction.Minimum[1]), Is.LessThan(1e-3)); |
||||
|
} |
||||
|
|
||||
|
[Test] |
||||
|
public void FindMinimum_BigRosenbrock_Overton() |
||||
|
{ |
||||
|
var obj = ObjectiveFunction.Gradient(BigRosenbrockFunction.Value, BigRosenbrockFunction.Gradient); |
||||
|
var solver = new LimitedMemoryBfgsMinimizer(1e-5, 1e-5, 1e-5, 5, 1000); |
||||
|
var result = solver.FindMinimum(obj, new DenseVector(new[] { -0.9 * 100.0, -0.5 * 100.0 })); |
||||
|
|
||||
|
Assert.That(Math.Abs(result.MinimizingPoint[0] - BigRosenbrockFunction.Minimum[0]), Is.LessThan(1e-3)); |
||||
|
Assert.That(Math.Abs(result.MinimizingPoint[1] - BigRosenbrockFunction.Minimum[1]), Is.LessThan(1e-3)); |
||||
|
} |
||||
|
|
||||
|
private class MghTestCaseEnumerator : IEnumerable<ITestCaseData> |
||||
|
{ |
||||
|
public IEnumerator<ITestCaseData> GetEnumerator() |
||||
|
{ |
||||
|
return |
||||
|
RosenbrockFunction2.TestCases |
||||
|
.Concat(BealeFunction.TestCases) |
||||
|
.Concat(HelicalValleyFunction.TestCases) |
||||
|
.Concat(MeyerFunction.TestCases) |
||||
|
.Concat(PowellSingularFunction.TestCases) |
||||
|
.Concat(WoodFunction.TestCases) |
||||
|
.Concat(BrownAndDennisFunction.TestCases) |
||||
|
.Where(x => x.IsUnbounded) |
||||
|
.Select<TestCase, ITestCaseData>(x => new TestCaseData(x) |
||||
|
.SetName(x.FullName) |
||||
|
) |
||||
|
.GetEnumerator(); |
||||
|
} |
||||
|
|
||||
|
IEnumerator IEnumerable.GetEnumerator() |
||||
|
{ |
||||
|
return this.GetEnumerator(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Test] |
||||
|
[TestCaseSource(typeof(MghTestCaseEnumerator))] |
||||
|
public void Mgh_Tests(TestFunctions.TestCase test_case) |
||||
|
{ |
||||
|
var obj = new MghObjectiveFunction(test_case.Function, true, true); |
||||
|
var solver = new LimitedMemoryBfgsMinimizer(1e-8, 1e-8, 1e-8, 5, 1000); |
||||
|
|
||||
|
var result = solver.FindMinimum(obj, test_case.InitialGuess); |
||||
|
|
||||
|
if (test_case.MinimizingPoint != null) |
||||
|
{ |
||||
|
Assert.That((result.MinimizingPoint - test_case.MinimizingPoint).L2Norm(), Is.LessThan(1e-3)); |
||||
|
} |
||||
|
|
||||
|
var val1 = result.FunctionInfoAtMinimum.Value; |
||||
|
var val2 = test_case.MinimalValue; |
||||
|
var abs_min = Math.Min(Math.Abs(val1), Math.Abs(val2)); |
||||
|
var abs_err = Math.Abs(val1 - val2); |
||||
|
var rel_err = abs_err / abs_min; |
||||
|
var success = (abs_min <= 1 && abs_err < 1e-3) || (abs_min > 1 && rel_err < 1e-3); |
||||
|
Assert.That(success, "Minimal function value is not as expected."); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue