Browse Source

Fixed an index out of bounds issue when calculating BFGS minimizer with one variable.

Moved a check to the start of a loop in QuadraticGradientProjectionSearch so that we exit earlier with one variable, and added a test that would have caught this issue.

Fixes github issue 510
pull/858/head
Shiney 5 years ago
parent
commit
3975772423
  1. 41
      src/Numerics.Tests/OptimizationTests/BfgsMinimizerTests.cs
  2. 10
      src/Numerics/Optimization/QuadraticGradientProjectionSearch.cs

41
src/Numerics.Tests/OptimizationTests/BfgsMinimizerTests.cs

@ -36,6 +36,8 @@ using MathNet.Numerics.UnitTests.OptimizationTests.TestFunctions;
using System.Collections.Generic;
using System.Collections;
using NUnit.Framework.Interfaces;
using MathNet.Numerics.LinearAlgebra;
using MathNet.Numerics.Optimization.ObjectiveFunctions;
namespace MathNet.Numerics.UnitTests.OptimizationTests
{
@ -155,5 +157,44 @@ namespace MathNet.Numerics.UnitTests.OptimizationTests
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.");
}
[Test]
public void BfgsMinimizer_MinimizesProperlyWhenConstrainedInOneVariable()
{
// Test comes from github issue 510
// https://github.com/mathnet/mathnet-numerics/issues/510
Func<Vector<double>, double> function = (Vector<double> vectorArg) =>
{
double x = vectorArg[0];
double y = -1.0 * Math.Exp(-x * x);
return y;
};
double gradientTolerance = 1e-10;
double parameterTolerance = 1e-10;
double functionProgressTolerance = 1e-10;
int maxIterations = 1000;
var initialGuess = new DenseVector(new[] { -1.0 });
// Bad Bound
var lowerBound = new DenseVector(new[] { -2.0 });
var upperBound = new DenseVector(new[] { 2.0 });
var objective = ObjectiveFunction.Value(function);
var objectiveWithGradient = new ForwardDifferenceGradientObjectiveFunction(objective, lowerBound, upperBound);
var algorithm = new BfgsBMinimizer(gradientTolerance, parameterTolerance, functionProgressTolerance, maxIterations);
var result = algorithm.FindMinimum(objectiveWithGradient, lowerBound, upperBound, initialGuess);
var resultVector = result.MinimizingPoint;
double xResult = resultVector[0];
// (actual minimum at zero)
var abs_err = Math.Abs(xResult);
var success = abs_err < 1e-6;
Assert.That(success, "Minimal function value is not as expected.");
}
}
}

10
src/Numerics/Optimization/QuadraticGradientProjectionSearch.cs

@ -80,6 +80,11 @@ namespace MathNet.Numerics.Optimization
// while minimum of the last quadratic piece observed is beyond the interval searched
while (true)
{
if (jj + 1 >= orderedBreakpoint.Count - 1)
{
isFixed[isFixed.Count - 1] = true;
return new GradientProjectionResult(x + maxS * d, lowerBound.Count, isFixed);
}
// update data to the beginning of the interval we're searching
jj += 1;
x = x + d * maxS;
@ -104,11 +109,6 @@ namespace MathNet.Numerics.Optimization
if (sMin < maxS)
return new GradientProjectionResult(x + sMin * d, fixedCount, isFixed);
else if (jj + 1 >= orderedBreakpoint.Count - 1)
{
isFixed[isFixed.Count - 1] = true;
return new GradientProjectionResult(x + maxS * d, lowerBound.Count, isFixed);
}
}
}

Loading…
Cancel
Save