forked from tsai/mathnet-numerics
committed by
GitHub
60 changed files with 6025 additions and 10 deletions
@ -0,0 +1,305 @@ |
|||
// <copyright file="BfgsTest.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-2016 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 MathNet.Numerics.LinearAlgebra; |
|||
using MathNet.Numerics.LinearAlgebra.Double; |
|||
using MathNet.Numerics.Optimization.LineSearch; |
|||
|
|||
namespace MathNet.Numerics.Optimization |
|||
{ |
|||
/// <summary>
|
|||
/// Broyden–Fletcher–Goldfarb–Shanno Bounded (BFGS-B) algorithm is an iterative method for solving box-constrained nonlinear optimization problems
|
|||
/// http://www.ece.northwestern.edu/~nocedal/PSfiles/limited.ps.gz
|
|||
/// </summary>
|
|||
public class BfgsBMinimizer : BfgsMinimizerBase |
|||
{ |
|||
public BfgsBMinimizer(double gradientTolerance, double parameterTolerance, double functionProgressTolerance, int maximumIterations = 1000) |
|||
: base(gradientTolerance,parameterTolerance,functionProgressTolerance,maximumIterations) |
|||
{ |
|||
} |
|||
|
|||
/// <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="lowerBound">The lower bound</param>
|
|||
/// <param name="upperBound">The upper bound</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> lowerBound, Vector<double> upperBound, Vector<double> initialGuess) |
|||
{ |
|||
_lowerBound = lowerBound; |
|||
_upperBound = upperBound; |
|||
if (!objective.IsGradientSupported) |
|||
throw new IncompatibleObjectiveException("Gradient not supported in objective function, but required for BFGS minimization."); |
|||
|
|||
// Check that dimensions match
|
|||
if (lowerBound.Count != upperBound.Count || lowerBound.Count != initialGuess.Count) |
|||
throw new ArgumentException("Dimensions of bounds and/or initial guess do not match."); |
|||
|
|||
// Check that initial guess is feasible
|
|||
for (int ii = 0; ii < initialGuess.Count; ++ii) |
|||
if (initialGuess[ii] < lowerBound[ii] || initialGuess[ii] > upperBound[ii]) |
|||
throw new ArgumentException("Initial guess is not in the feasible region"); |
|||
|
|||
objective.EvaluateAt(initialGuess); |
|||
ValidateGradientAndObjective(objective); |
|||
|
|||
// Check that we're not already done
|
|||
MinimizationResult.ExitCondition currentExitCondition = ExitCriteriaSatisfied(objective, null, 0); |
|||
if (currentExitCondition != MinimizationResult.ExitCondition.None) |
|||
return new MinimizationResult(objective, 0, currentExitCondition); |
|||
|
|||
// Set up line search algorithm
|
|||
var lineSearcher = new StrongWolfeLineSearch(1e-4, 0.9, Math.Max(ParameterTolerance, 1e-5), maxIterations: 1000); |
|||
|
|||
// Declare state variables
|
|||
Vector<double> reducedSolution1, reducedGradient, reducedInitialPoint, reducedCauchyPoint, solution1; |
|||
Matrix<double> reducedHessian; |
|||
List<int> reducedMap; |
|||
|
|||
// First step
|
|||
var pseudoHessian = CreateMatrix.DiagonalIdentity<double>(initialGuess.Count); |
|||
|
|||
// Determine active set
|
|||
var gradientProjectionResult = QuadraticGradientProjectionSearch.Search(objective.Point, objective.Gradient, pseudoHessian, lowerBound, upperBound); |
|||
var cauchyPoint = gradientProjectionResult.CauchyPoint; |
|||
var fixedCount = gradientProjectionResult.FixedCount; |
|||
var isFixed = gradientProjectionResult.IsFixed; |
|||
var freeCount = lowerBound.Count - fixedCount; |
|||
|
|||
if (freeCount > 0) |
|||
{ |
|||
reducedGradient = new DenseVector(freeCount); |
|||
reducedHessian = new DenseMatrix(freeCount, freeCount); |
|||
reducedMap = new List<int>(freeCount); |
|||
reducedInitialPoint = new DenseVector(freeCount); |
|||
reducedCauchyPoint = new DenseVector(freeCount); |
|||
|
|||
CreateReducedData(objective.Point, cauchyPoint, isFixed, lowerBound, upperBound, objective.Gradient, pseudoHessian, reducedInitialPoint, reducedCauchyPoint, reducedGradient, reducedHessian, reducedMap); |
|||
|
|||
// Determine search direction and maximum step size
|
|||
reducedSolution1 = reducedInitialPoint + reducedHessian.Cholesky().Solve(-reducedGradient); |
|||
|
|||
solution1 = ReducedToFull(reducedMap, reducedSolution1, cauchyPoint); |
|||
} |
|||
else |
|||
{ |
|||
solution1 = cauchyPoint; |
|||
} |
|||
|
|||
var directionFromCauchy = solution1 - cauchyPoint; |
|||
var maxStepFromCauchyPoint = FindMaxStep(cauchyPoint, directionFromCauchy, lowerBound, upperBound); |
|||
|
|||
var solution2 = cauchyPoint + Math.Min(maxStepFromCauchyPoint, 1.0)*directionFromCauchy; |
|||
|
|||
var lineSearchDirection = solution2 - objective.Point; |
|||
var maxLineSearchStep = FindMaxStep(objective.Point, lineSearchDirection, lowerBound, upperBound); |
|||
var estStepSize = -objective.Gradient*lineSearchDirection/(lineSearchDirection*pseudoHessian*lineSearchDirection); |
|||
|
|||
var startingStepSize = Math.Min(Math.Max(estStepSize, 1.0), maxLineSearchStep); |
|||
|
|||
// Line search
|
|||
LineSearchResult lineSearchResult; |
|||
try |
|||
{ |
|||
lineSearchResult = lineSearcher.FindConformingStep(objective, lineSearchDirection, startingStepSize, upperBound: maxLineSearchStep); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
throw new InnerOptimizationException("Line search failed.", e); |
|||
} |
|||
|
|||
var previousPoint = objective.Fork(); |
|||
var candidatePoint = lineSearchResult.FunctionInfoAtMinimum; |
|||
var gradient = candidatePoint.Gradient; |
|||
var step = candidatePoint.Point - initialGuess; |
|||
|
|||
// Subsequent steps
|
|||
int totalLineSearchSteps = lineSearchResult.Iterations; |
|||
int iterationsWithNontrivialLineSearch = lineSearchResult.Iterations > 0 ? 0 : 1; |
|||
|
|||
int iterations = DoBfgsUpdate(ref currentExitCondition, lineSearcher, ref pseudoHessian, ref lineSearchDirection, ref previousPoint, ref lineSearchResult, ref candidatePoint, ref step, ref totalLineSearchSteps, ref iterationsWithNontrivialLineSearch); |
|||
|
|||
if (iterations == MaximumIterations && currentExitCondition == MinimizationResult.ExitCondition.None) |
|||
throw new MaximumIterationsException(string.Format("Maximum iterations ({0}) reached.", MaximumIterations)); |
|||
|
|||
return new MinimizationWithLineSearchResult(candidatePoint, iterations, currentExitCondition, totalLineSearchSteps, iterationsWithNontrivialLineSearch); |
|||
} |
|||
|
|||
protected override Vector<double> CalculateSearchDirection(ref Matrix<double> pseudoHessian, |
|||
out double maxLineSearchStep, |
|||
out double startingStepSize, |
|||
IObjectiveFunction previousPoint, |
|||
IObjectiveFunction candidatePoint, |
|||
Vector<double> step) |
|||
{ |
|||
Vector<double> lineSearchDirection; |
|||
var y = candidatePoint.Gradient - previousPoint.Gradient; |
|||
|
|||
double sy = step * y; |
|||
if (sy > 0.0) // only do update if it will create a positive definite matrix
|
|||
{ |
|||
double sts = step * step; |
|||
|
|||
var Hs = pseudoHessian * step; |
|||
var sHs = step * pseudoHessian * step; |
|||
pseudoHessian = pseudoHessian + y.OuterProduct(y) * (1.0 / sy) - Hs.OuterProduct(Hs) * (1.0 / sHs); |
|||
} |
|||
else |
|||
{ |
|||
//pseudo_hessian = LinearAlgebra.Double.DiagonalMatrix.Identity(initial_guess.Count);
|
|||
} |
|||
|
|||
// Determine active set
|
|||
var gradientProjectionResult = QuadraticGradientProjectionSearch.Search(candidatePoint.Point, candidatePoint.Gradient, pseudoHessian, _lowerBound, _upperBound); |
|||
var cauchyPoint = gradientProjectionResult.CauchyPoint; |
|||
var fixedCount = gradientProjectionResult.FixedCount; |
|||
var isFixed = gradientProjectionResult.IsFixed; |
|||
var freeCount = _lowerBound.Count - fixedCount; |
|||
Vector<double> solution1; |
|||
if (freeCount > 0) |
|||
{ |
|||
var reducedGradient = new DenseVector(freeCount); |
|||
var reducedHessian = new DenseMatrix(freeCount, freeCount); |
|||
var reducedMap = new List<int>(freeCount); |
|||
var reducedInitialPoint = new DenseVector(freeCount); |
|||
var reducedCauchyPoint = new DenseVector(freeCount); |
|||
|
|||
CreateReducedData(candidatePoint.Point, cauchyPoint, isFixed, _lowerBound, _upperBound, candidatePoint.Gradient, pseudoHessian, reducedInitialPoint, reducedCauchyPoint, reducedGradient, reducedHessian, reducedMap); |
|||
|
|||
// Determine search direction and maximum step size
|
|||
Vector<double> reducedSolution1 = reducedInitialPoint + reducedHessian.Cholesky().Solve(-reducedGradient); |
|||
|
|||
solution1 = ReducedToFull(reducedMap, reducedSolution1, cauchyPoint); |
|||
} |
|||
else |
|||
{ |
|||
solution1 = cauchyPoint; |
|||
} |
|||
|
|||
var directionFromCauchy = solution1 - cauchyPoint; |
|||
var maxStepFromCauchyPoint = FindMaxStep(cauchyPoint, directionFromCauchy, _lowerBound, _upperBound); |
|||
|
|||
var solution2 = cauchyPoint + Math.Min(maxStepFromCauchyPoint, 1.0) * directionFromCauchy; |
|||
|
|||
lineSearchDirection = solution2 - candidatePoint.Point; |
|||
maxLineSearchStep = FindMaxStep(candidatePoint.Point, lineSearchDirection, _lowerBound, _upperBound); |
|||
|
|||
if (maxLineSearchStep == 0.0) |
|||
{ |
|||
lineSearchDirection = cauchyPoint - candidatePoint.Point; |
|||
maxLineSearchStep = FindMaxStep(candidatePoint.Point, lineSearchDirection, _lowerBound, _upperBound); |
|||
} |
|||
|
|||
double estStepSize = -candidatePoint.Gradient * lineSearchDirection / (lineSearchDirection * pseudoHessian * lineSearchDirection); |
|||
|
|||
startingStepSize = Math.Min(Math.Max(estStepSize, 1.0), maxLineSearchStep); |
|||
return lineSearchDirection; |
|||
} |
|||
|
|||
private static Vector<double> ReducedToFull(List<int> reducedMap, Vector<double> reducedVector, Vector<double> fullVector) |
|||
{ |
|||
var output = fullVector.Clone(); |
|||
for (int ii = 0; ii < reducedMap.Count; ++ii) |
|||
output[reducedMap[ii]] = reducedVector[ii]; |
|||
return output; |
|||
} |
|||
|
|||
private Vector<double> _lowerBound; |
|||
private Vector<double> _upperBound; |
|||
|
|||
private static double FindMaxStep(Vector<double> startingPoint, Vector<double> searchDirection, Vector<double> lowerBound, Vector<double> upperBound) |
|||
{ |
|||
double maxStep = Double.PositiveInfinity; |
|||
for (int ii = 0; ii < startingPoint.Count; ++ii) |
|||
{ |
|||
double paramMaxStep; |
|||
if (searchDirection[ii] > 0) |
|||
paramMaxStep = (upperBound[ii] - startingPoint[ii])/searchDirection[ii]; |
|||
else if (searchDirection[ii] < 0) |
|||
paramMaxStep = (startingPoint[ii] - lowerBound[ii])/-searchDirection[ii]; |
|||
else |
|||
paramMaxStep = Double.PositiveInfinity; |
|||
|
|||
if (paramMaxStep < maxStep) |
|||
maxStep = paramMaxStep; |
|||
} |
|||
return maxStep; |
|||
} |
|||
|
|||
private static void CreateReducedData(Vector<double> initialPoint, Vector<double> cauchyPoint, List<bool> isFixed, Vector<double> lowerBound, Vector<double> upperBound, Vector<double> gradient, Matrix<double> pseudoHessian, Vector<double> reducedInitialPoint, Vector<double> reducedCauchyPoint, Vector<double> reducedGradient, Matrix<double> reducedHessian, List<int> reducedMap) |
|||
{ |
|||
int ll = 0; |
|||
for (int ii = 0; ii < lowerBound.Count; ++ii) |
|||
{ |
|||
if (!isFixed[ii]) |
|||
{ |
|||
// hessian
|
|||
int mm = 0; |
|||
for (int jj = 0; jj < lowerBound.Count; ++jj) |
|||
{ |
|||
if (!isFixed[jj]) |
|||
{ |
|||
reducedHessian[ll, mm++] = pseudoHessian[ii, jj]; |
|||
} |
|||
} |
|||
|
|||
// gradient
|
|||
reducedInitialPoint[ll] = initialPoint[ii]; |
|||
reducedCauchyPoint[ll] = cauchyPoint[ii]; |
|||
reducedGradient[ll] = gradient[ii]; |
|||
ll += 1; |
|||
reducedMap.Add(ii); |
|||
|
|||
} |
|||
} |
|||
} |
|||
|
|||
protected override double GetProjectedGradient(IObjectiveFunction candidatePoint, int ii) |
|||
{ |
|||
double projectedGradient; |
|||
bool atLowerBound = candidatePoint.Point[ii] - _lowerBound[ii] < VerySmall; |
|||
bool atUpperBound = _upperBound[ii] - candidatePoint.Point[ii] < VerySmall; |
|||
|
|||
if (atLowerBound && atUpperBound) |
|||
projectedGradient = 0.0; |
|||
else if (atLowerBound) |
|||
projectedGradient = Math.Min(candidatePoint.Gradient[ii], 0.0); |
|||
else if (atUpperBound) |
|||
projectedGradient = Math.Max(candidatePoint.Gradient[ii], 0.0); |
|||
else |
|||
projectedGradient = base.GetProjectedGradient(candidatePoint, ii); |
|||
return projectedGradient; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,142 @@ |
|||
// <copyright file="BfgsTest.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-2016 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 MathNet.Numerics.LinearAlgebra; |
|||
using MathNet.Numerics.Optimization.LineSearch; |
|||
|
|||
namespace MathNet.Numerics.Optimization |
|||
{ |
|||
/// <summary>
|
|||
/// Broyden–Fletcher–Goldfarb–Shanno (BFGS) algorithm is an iterative method for solving unconstrained nonlinear optimization problems
|
|||
/// </summary>
|
|||
public class BfgsMinimizer : BfgsMinimizerBase |
|||
{ |
|||
/// <summary>
|
|||
/// Creates BFGS minimizer
|
|||
/// </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>
|
|||
public BfgsMinimizer(double gradientTolerance, double parameterTolerance, double functionProgressTolerance, int maximumIterations=1000) |
|||
:base(gradientTolerance,parameterTolerance,functionProgressTolerance,maximumIterations) |
|||
{ |
|||
} |
|||
|
|||
/// <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 BFGS minimization."); |
|||
|
|||
objective.EvaluateAt(initialGuess); |
|||
ValidateGradientAndObjective(objective); |
|||
|
|||
// Check that we're not already done
|
|||
MinimizationResult.ExitCondition currentExitCondition = ExitCriteriaSatisfied(objective, null, 0); |
|||
if (currentExitCondition != MinimizationResult.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 inversePseudoHessian = CreateMatrix.DenseIdentity<double>(initialGuess.Count); |
|||
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; |
|||
|
|||
// Subsequent steps
|
|||
Matrix<double> I = CreateMatrix.DiagonalIdentity<double>(initialGuess.Count); |
|||
int iterations; |
|||
int totalLineSearchSteps = lineSearchResult.Iterations; |
|||
int iterationsWithNontrivialLineSearch = lineSearchResult.Iterations > 0 ? 0 : 1; |
|||
iterations = DoBfgsUpdate(ref currentExitCondition, lineSearcher, ref inversePseudoHessian, ref lineSearchDirection, ref previousPoint, ref lineSearchResult, ref candidate, ref step, ref totalLineSearchSteps, ref iterationsWithNontrivialLineSearch); |
|||
|
|||
if (iterations == MaximumIterations && currentExitCondition == MinimizationResult.ExitCondition.None) |
|||
throw new MaximumIterationsException(String.Format("Maximum iterations ({0}) reached.", MaximumIterations)); |
|||
|
|||
return new MinimizationWithLineSearchResult(candidate, iterations, MinimizationResult.ExitCondition.AbsoluteGradient, totalLineSearchSteps, iterationsWithNontrivialLineSearch); |
|||
} |
|||
|
|||
protected override Vector<double> CalculateSearchDirection(ref Matrix<double> inversePseudoHessian, |
|||
out double maxLineSearchStep, |
|||
out double startingStepSize, |
|||
IObjectiveFunction previousPoint, |
|||
IObjectiveFunction candidate, |
|||
Vector<double> step) |
|||
{ |
|||
startingStepSize = 1.0; |
|||
maxLineSearchStep = double.PositiveInfinity; |
|||
|
|||
Vector<double> lineSearchDirection; |
|||
var y = candidate.Gradient - previousPoint.Gradient; |
|||
|
|||
double sy = step * y; |
|||
inversePseudoHessian = inversePseudoHessian + ((sy + y * inversePseudoHessian * y) / Math.Pow(sy, 2.0)) * step.OuterProduct(step) - ((inversePseudoHessian * y.ToColumnMatrix()) * step.ToRowMatrix() + step.ToColumnMatrix() * (y.ToRowMatrix() * inversePseudoHessian)) * (1.0 / sy); |
|||
lineSearchDirection = -inversePseudoHessian * candidate.Gradient; |
|||
|
|||
if (lineSearchDirection * candidate.Gradient >= 0.0) |
|||
{ |
|||
lineSearchDirection = -candidate.Gradient; |
|||
inversePseudoHessian = CreateMatrix.DenseIdentity<double>(candidate.Point.Count); |
|||
} |
|||
|
|||
return lineSearchDirection; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,158 @@ |
|||
// <copyright file="BfgsTest.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-2016 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 BfgsMinimizerBase |
|||
{ |
|||
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 BFGS 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>
|
|||
public BfgsMinimizerBase(double gradientTolerance, double parameterTolerance, double functionProgressTolerance, int maximumIterations) |
|||
{ |
|||
GradientTolerance = gradientTolerance; |
|||
ParameterTolerance = parameterTolerance; |
|||
FunctionProgressTolerance = functionProgressTolerance; |
|||
MaximumIterations = maximumIterations; |
|||
} |
|||
|
|||
protected MinimizationResult.ExitCondition ExitCriteriaSatisfied(IObjectiveFunction candidatePoint, IObjectiveFunction 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 MinimizationResult.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 MinimizationResult.ExitCondition.LackOfProgress; |
|||
} |
|||
|
|||
double functionChange = candidatePoint.Value - lastPoint.Value; |
|||
if (iterations > 500 && functionChange < 0 && Math.Abs(functionChange) < FunctionProgressTolerance) |
|||
return MinimizationResult.ExitCondition.LackOfProgress; |
|||
} |
|||
|
|||
return MinimizationResult.ExitCondition.None; |
|||
} |
|||
|
|||
protected virtual double GetProjectedGradient(IObjectiveFunction candidatePoint, int ii) |
|||
{ |
|||
return candidatePoint.Gradient[ii]; |
|||
} |
|||
|
|||
protected void ValidateGradientAndObjective(IObjectiveFunction 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); |
|||
} |
|||
|
|||
protected int DoBfgsUpdate(ref MinimizationResult.ExitCondition currentExitCondition, WolfeLineSearch lineSearcher, ref Matrix<double> inversePseudoHessian, ref Vector<double> lineSearchDirection, ref IObjectiveFunction previousPoint, ref LineSearchResult lineSearchResult, ref IObjectiveFunction candidate, ref Vector<double> step, ref int totalLineSearchSteps, ref int iterationsWithNontrivialLineSearch) |
|||
{ |
|||
int iterations; |
|||
for (iterations = 1; iterations < MaximumIterations; ++iterations) |
|||
{ |
|||
double startingStepSize; |
|||
double maxLineSearchStep; |
|||
lineSearchDirection = CalculateSearchDirection(ref inversePseudoHessian, out maxLineSearchStep, out startingStepSize, previousPoint, candidate, step); |
|||
|
|||
try |
|||
{ |
|||
lineSearchResult = lineSearcher.FindConformingStep(candidate, lineSearchDirection, startingStepSize, maxLineSearchStep); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
throw new InnerOptimizationException("Line search failed.", e); |
|||
} |
|||
|
|||
iterationsWithNontrivialLineSearch += lineSearchResult.Iterations > 0 ? 1 : 0; |
|||
totalLineSearchSteps += lineSearchResult.Iterations; |
|||
|
|||
step = lineSearchResult.FunctionInfoAtMinimum.Point - candidate.Point; |
|||
previousPoint = candidate; |
|||
candidate = lineSearchResult.FunctionInfoAtMinimum; |
|||
|
|||
currentExitCondition = ExitCriteriaSatisfied(candidate, previousPoint, iterations); |
|||
if (currentExitCondition != MinimizationResult.ExitCondition.None) |
|||
break; |
|||
} |
|||
|
|||
return iterations; |
|||
} |
|||
|
|||
protected abstract Vector<double> CalculateSearchDirection(ref Matrix<double> inversePseudoHessian, |
|||
out double maxLineSearchStep, |
|||
out double startingStepSize, |
|||
IObjectiveFunction previousPoint, |
|||
IObjectiveFunction candidate, |
|||
Vector<double> step); |
|||
} |
|||
} |
|||
@ -0,0 +1,110 @@ |
|||
// <copyright file="BfgsSolver.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 System; |
|||
using MathNet.Numerics.LinearAlgebra; |
|||
using MathNet.Numerics.LinearAlgebra.Double; |
|||
using MathNet.Numerics.Optimization.LineSearch; |
|||
|
|||
namespace MathNet.Numerics.Optimization |
|||
{ |
|||
/// <summary>
|
|||
/// Broyden-Fletcher-Goldfarb-Shanno solver for finding function minima
|
|||
/// See http://en.wikipedia.org/wiki/Broyden%E2%80%93Fletcher%E2%80%93Goldfarb%E2%80%93Shanno_algorithm
|
|||
/// Inspired by implementation: https://github.com/PatWie/CppNumericalSolvers/blob/master/src/BfgsSolver.cpp
|
|||
/// </summary>
|
|||
public static class BfgsSolver |
|||
{ |
|||
private const double GradientTolerance = 1e-5; |
|||
private const int MaxIterations = 100000; |
|||
|
|||
/// <summary>
|
|||
/// Finds a minimum of a function by the BFGS quasi-Newton method
|
|||
/// This uses the function and it's gradient (partial derivatives in each direction) and approximates the Hessian
|
|||
/// </summary>
|
|||
/// <param name="initialGuess">An initial guess</param>
|
|||
/// <param name="functionValue">Evaluates the function at a point</param>
|
|||
/// <param name="functionGradient">Evaluates the gradient of the function at a point</param>
|
|||
/// <returns>The minimum found</returns>
|
|||
public static Vector<double> Solve(Vector initialGuess, Func<Vector<double>, double> functionValue, Func<Vector<double>, Vector<double>> functionGradient) |
|||
{ |
|||
var objectiveFunction = ObjectiveFunction.Gradient(functionValue, functionGradient); |
|||
objectiveFunction.EvaluateAt(initialGuess); |
|||
|
|||
int dim = initialGuess.Count; |
|||
int iter = 0; |
|||
// H represents the approximation of the inverse hessian matrix
|
|||
// it is updated via the Sherman–Morrison formula (http://en.wikipedia.org/wiki/Sherman%E2%80%93Morrison_formula)
|
|||
Matrix<double> H = DenseMatrix.CreateIdentity(dim); |
|||
|
|||
Vector<double> x = initialGuess; |
|||
Vector<double> x_old = x; |
|||
Vector<double> grad; |
|||
WolfeLineSearch wolfeLineSearch = new WeakWolfeLineSearch(1e-4, 0.9, 1e-5, 200); |
|||
do |
|||
{ |
|||
// search along the direction of the gradient
|
|||
grad = objectiveFunction.Gradient; |
|||
Vector<double> p = -1 * H * grad; |
|||
var lineSearchResult = wolfeLineSearch.FindConformingStep(objectiveFunction, p, 1.0); |
|||
double rate = lineSearchResult.FinalStep; |
|||
x = x + rate * p; |
|||
Vector<double> grad_old = grad; |
|||
|
|||
// update the gradient
|
|||
objectiveFunction.EvaluateAt(x); |
|||
grad = objectiveFunction.Gradient;// functionGradient(x);
|
|||
|
|||
Vector<double> s = x - x_old; |
|||
Vector<double> y = grad - grad_old; |
|||
|
|||
double rho = 1.0 / (y * s); |
|||
if (iter == 0) |
|||
{ |
|||
// set up an initial hessian
|
|||
H = (y * s) / (y * y) * DenseMatrix.CreateIdentity(dim); |
|||
} |
|||
|
|||
var sM = s.ToColumnMatrix(); |
|||
var yM = y.ToColumnMatrix(); |
|||
|
|||
// Update the estimate of the hessian
|
|||
H = H |
|||
- rho * (sM * (yM.TransposeThisAndMultiply(H)) + (H * yM).TransposeAndMultiply(sM)) |
|||
+ rho * rho * (y.DotProduct(H * y) + 1.0 / rho) * (sM.TransposeAndMultiply(sM)); |
|||
x_old = x; |
|||
iter++; |
|||
} |
|||
while ((grad.InfinityNorm() > GradientTolerance) && (iter < MaxIterations)); |
|||
|
|||
return x; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,115 @@ |
|||
using System; |
|||
using MathNet.Numerics.LinearAlgebra; |
|||
using MathNet.Numerics.Optimization.LineSearch; |
|||
|
|||
namespace MathNet.Numerics.Optimization |
|||
{ |
|||
public class ConjugateGradientMinimizer |
|||
{ |
|||
public double GradientTolerance { get; set; } |
|||
public int MaximumIterations { get; set; } |
|||
|
|||
public ConjugateGradientMinimizer(double gradientTolerance, int maximumIterations) |
|||
{ |
|||
GradientTolerance = gradientTolerance; |
|||
MaximumIterations = maximumIterations; |
|||
} |
|||
|
|||
public MinimizationResult FindMinimum(IObjectiveFunction objective, Vector<double> initialGuess) |
|||
{ |
|||
if (!objective.IsGradientSupported) |
|||
throw new IncompatibleObjectiveException("Gradient not supported in objective function, but required for ConjugateGradient minimization."); |
|||
|
|||
objective.EvaluateAt(initialGuess); |
|||
var gradient = objective.Gradient; |
|||
ValidateGradient(objective); |
|||
|
|||
// Check that we're not already done
|
|||
if (ExitCriteriaSatisfied(initialGuess, gradient)) |
|||
return new MinimizationResult(objective, 0, MinimizationResult.ExitCondition.AbsoluteGradient); |
|||
|
|||
// Set up line search algorithm
|
|||
var lineSearcher = new WeakWolfeLineSearch(1e-4, 0.1, 1e-4, 1000); |
|||
|
|||
// First step
|
|||
var steepestDirection = -gradient; |
|||
var searchDirection = steepestDirection; |
|||
double initialStepSize = 100 * GradientTolerance / (gradient * gradient); |
|||
|
|||
LineSearchResult result; |
|||
try |
|||
{ |
|||
result = lineSearcher.FindConformingStep(objective, searchDirection, initialStepSize); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
throw new InnerOptimizationException("Line search failed.", e); |
|||
} |
|||
|
|||
objective = result.FunctionInfoAtMinimum; |
|||
ValidateGradient(objective); |
|||
|
|||
double stepSize = result.FinalStep; |
|||
|
|||
// Subsequent steps
|
|||
int iterations = 1; |
|||
int totalLineSearchSteps = result.Iterations; |
|||
int iterationsWithNontrivialLineSearch = result.Iterations > 0 ? 0 : 1; |
|||
int steepestDescentResets = 0; |
|||
while (!ExitCriteriaSatisfied(objective.Point, objective.Gradient) && iterations < MaximumIterations) |
|||
{ |
|||
var previousSteepestDirection = steepestDirection; |
|||
steepestDirection = -objective.Gradient; |
|||
var searchDirectionAdjuster = Math.Max(0, steepestDirection*(steepestDirection - previousSteepestDirection)/(previousSteepestDirection*previousSteepestDirection)); |
|||
searchDirection = steepestDirection + searchDirectionAdjuster * searchDirection; |
|||
if (searchDirection * objective.Gradient >= 0) |
|||
{ |
|||
searchDirection = steepestDirection; |
|||
steepestDescentResets += 1; |
|||
} |
|||
|
|||
try |
|||
{ |
|||
result = lineSearcher.FindConformingStep(objective, searchDirection, stepSize); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
throw new InnerOptimizationException("Line search failed.", e); |
|||
} |
|||
|
|||
iterationsWithNontrivialLineSearch += result.Iterations == 0 ? 1 : 0; |
|||
totalLineSearchSteps += result.Iterations; |
|||
stepSize = result.FinalStep; |
|||
objective = result.FunctionInfoAtMinimum; |
|||
iterations += 1; |
|||
} |
|||
|
|||
if (iterations == MaximumIterations) |
|||
{ |
|||
throw new MaximumIterationsException(String.Format("Maximum iterations ({0}) reached.", MaximumIterations)); |
|||
} |
|||
|
|||
return new MinimizationWithLineSearchResult(objective, iterations, MinimizationResult.ExitCondition.AbsoluteGradient, totalLineSearchSteps, iterationsWithNontrivialLineSearch); |
|||
} |
|||
|
|||
bool ExitCriteriaSatisfied(Vector<double> candidatePoint, Vector<double> gradient) |
|||
{ |
|||
return gradient.Norm(2.0) < GradientTolerance; |
|||
} |
|||
|
|||
void ValidateGradient(IObjectiveFunction objective) |
|||
{ |
|||
foreach (var x in objective.Gradient) |
|||
{ |
|||
if (Double.IsNaN(x) || Double.IsInfinity(x)) |
|||
throw new EvaluationException("Non-finite gradient returned.", objective); |
|||
} |
|||
} |
|||
|
|||
void ValidateObjective(IObjectiveFunction objective) |
|||
{ |
|||
if (Double.IsNaN(objective.Value) || Double.IsInfinity(objective.Value)) |
|||
throw new EvaluationException("Non-finite objective function returned.", objective); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,52 @@ |
|||
using System; |
|||
|
|||
namespace MathNet.Numerics.Optimization |
|||
{ |
|||
public class OptimizationException : Exception |
|||
{ |
|||
public OptimizationException(string message) |
|||
: base(message) { } |
|||
|
|||
public OptimizationException(string message, Exception innerException) |
|||
: base(message, innerException) { } |
|||
} |
|||
|
|||
public class MaximumIterationsException : OptimizationException |
|||
{ |
|||
public MaximumIterationsException(string message) |
|||
: base(message) { } |
|||
} |
|||
|
|||
public class EvaluationException : OptimizationException |
|||
{ |
|||
public IObjectiveFunction ObjectiveFunction { get; private set; } |
|||
|
|||
public EvaluationException(string message, IObjectiveFunction eval) |
|||
: base(message) |
|||
{ |
|||
ObjectiveFunction = eval; |
|||
} |
|||
|
|||
public EvaluationException(string message, IObjectiveFunction eval, Exception innerException) |
|||
: base(message, innerException) |
|||
{ |
|||
ObjectiveFunction = eval; |
|||
} |
|||
|
|||
} |
|||
|
|||
public class InnerOptimizationException : OptimizationException |
|||
{ |
|||
public InnerOptimizationException(string message) |
|||
: base(message) { } |
|||
|
|||
public InnerOptimizationException(string message, Exception inner_exception) |
|||
: base(message, inner_exception) { } |
|||
} |
|||
|
|||
public class IncompatibleObjectiveException : OptimizationException |
|||
{ |
|||
public IncompatibleObjectiveException(string message) |
|||
: base(message) { } |
|||
} |
|||
} |
|||
@ -0,0 +1,107 @@ |
|||
using System; |
|||
|
|||
namespace MathNet.Numerics.Optimization |
|||
{ |
|||
public class GoldenSectionMinimizer |
|||
{ |
|||
public double XTolerance { get; set; } |
|||
public int MaximumIterations { get; set; } |
|||
public int MaximumExpansionSteps { get; set; } |
|||
public double LowerExpansionFactor { get; set; } |
|||
public double UpperExpansionFactor { get; set; } |
|||
|
|||
public GoldenSectionMinimizer(double xTolerance = 1e-5, int maxIterations = 1000, int maxExpansionSteps = 10, double lowerExpansionFactor = 2.0, double upperExpansionFactor = 2.0) |
|||
{ |
|||
XTolerance = xTolerance; |
|||
MaximumIterations = maxIterations; |
|||
MaximumExpansionSteps = maxExpansionSteps; |
|||
LowerExpansionFactor = lowerExpansionFactor; |
|||
UpperExpansionFactor = upperExpansionFactor; |
|||
} |
|||
|
|||
public MinimizationResult1D FindMinimum(IObjectiveFunction1D objective, double lowerBound, double upperBound) |
|||
{ |
|||
if (upperBound <= lowerBound) |
|||
throw new OptimizationException("Lower bound must be lower than upper bound."); |
|||
|
|||
double middlePointX = lowerBound + (upperBound - lowerBound)/(1 + Constants.GoldenRatio); |
|||
IEvaluation1D lower = objective.Evaluate(lowerBound); |
|||
IEvaluation1D middle = objective.Evaluate(middlePointX); |
|||
IEvaluation1D upper = objective.Evaluate(upperBound); |
|||
|
|||
ValueChecker(lower.Value, lowerBound); |
|||
ValueChecker(middle.Value, middlePointX); |
|||
ValueChecker(upper.Value, upperBound); |
|||
|
|||
int expansion_steps = 0; |
|||
while ((expansion_steps < this.MaximumExpansionSteps) && (upper.Value < middle.Value || lower.Value < middle.Value)) |
|||
{ |
|||
if (lower.Value < middle.Value) |
|||
{ |
|||
lowerBound = 0.5*(upperBound + lowerBound) - this.LowerExpansionFactor*0.5*(upperBound - lowerBound); |
|||
lower = objective.Evaluate(lowerBound); |
|||
} |
|||
|
|||
if (upper.Value < middle.Value) |
|||
{ |
|||
upperBound = 0.5*(upperBound + lowerBound) + this.UpperExpansionFactor*0.5*(upperBound - lowerBound); |
|||
upper = objective.Evaluate(upperBound); |
|||
} |
|||
|
|||
middlePointX = lowerBound + (upperBound - lowerBound)/(1 + Constants.GoldenRatio); |
|||
middle = objective.Evaluate(middlePointX); |
|||
|
|||
expansion_steps += 1; |
|||
} |
|||
|
|||
if (upper.Value < middle.Value || lower.Value < middle.Value) |
|||
throw new OptimizationException("Lower and upper bounds do not necessarily bound a minimum."); |
|||
|
|||
int iterations = 0; |
|||
while (Math.Abs(upper.Point - lower.Point) > XTolerance && iterations < MaximumIterations) |
|||
{ |
|||
double testX = lower.Point + (upper.Point - middle.Point); |
|||
var test = objective.Evaluate(testX); |
|||
ValueChecker(test.Value, testX); |
|||
|
|||
if (test.Point < middle.Point) |
|||
{ |
|||
if (test.Value > middle.Value) |
|||
{ |
|||
lower = test; |
|||
} |
|||
else |
|||
{ |
|||
upper = middle; |
|||
middle = test; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
if (test.Value > middle.Value) |
|||
{ |
|||
upper = test; |
|||
} |
|||
else |
|||
{ |
|||
lower = middle; |
|||
middle = test; |
|||
} |
|||
} |
|||
|
|||
iterations += 1; |
|||
} |
|||
|
|||
if (iterations == MaximumIterations) |
|||
throw new MaximumIterationsException("Max iterations reached."); |
|||
|
|||
return new MinimizationResult1D(middle, iterations, MinimizationResult.ExitCondition.BoundTolerance); |
|||
} |
|||
|
|||
void ValueChecker(double value, double point) |
|||
{ |
|||
if (Double.IsNaN(value) || Double.IsInfinity(value)) |
|||
throw new Exception("Objective function returned non-finite value."); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
using MathNet.Numerics.LinearAlgebra; |
|||
|
|||
namespace MathNet.Numerics.Optimization |
|||
{ |
|||
/// <summary>
|
|||
/// Objective function with a frozen evaluation that must not be changed from the outside.
|
|||
/// </summary>
|
|||
public interface IObjectiveFunctionEvaluation |
|||
{ |
|||
/// <summary>Create a new unevaluated and independent copy of this objective function</summary>
|
|||
IObjectiveFunction CreateNew(); |
|||
|
|||
/// <summary>Create a new independent copy of this objective function, evaluated at the same point.</summary>
|
|||
IObjectiveFunction Fork(); |
|||
|
|||
Vector<double> Point { get; } |
|||
double Value { get; } |
|||
|
|||
bool IsGradientSupported { get; } |
|||
Vector<double> Gradient { get; } |
|||
|
|||
bool IsHessianSupported { get; } |
|||
Matrix<double> Hessian { get; } |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Objective function with a mutable evaluation.
|
|||
/// </summary>
|
|||
public interface IObjectiveFunction : IObjectiveFunctionEvaluation |
|||
{ |
|||
void EvaluateAt(Vector<double> point); |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using MathNet.Numerics.LinearAlgebra; |
|||
|
|||
namespace MathNet.Numerics.Optimization |
|||
{ |
|||
public interface IUnconstrainedMinimizer |
|||
{ |
|||
MinimizationResult FindMinimum(IObjectiveFunction objective, Vector<double> initialGuess); |
|||
} |
|||
} |
|||
@ -0,0 +1,43 @@ |
|||
// <copyright file="BfgsTest.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-2016 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>
|
|||
|
|||
namespace MathNet.Numerics.Optimization.LineSearch |
|||
{ |
|||
public class LineSearchResult : MinimizationResult |
|||
{ |
|||
public double FinalStep { get; private set; } |
|||
|
|||
public LineSearchResult(IObjectiveFunction functionInfo, int iterations, double finalStep, ExitCondition reasonForExit) |
|||
: base(functionInfo, iterations, reasonForExit) |
|||
{ |
|||
FinalStep = finalStep; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,50 @@ |
|||
// <copyright file="BfgsTest.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-2016 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; |
|||
|
|||
namespace MathNet.Numerics.Optimization.LineSearch |
|||
{ |
|||
public class StrongWolfeLineSearch : WolfeLineSearch |
|||
{ |
|||
public StrongWolfeLineSearch(double c1, double c2, double parameterTolerance, int maxIterations = 10) |
|||
: base(c1, c2, parameterTolerance, maxIterations) |
|||
{ |
|||
// Argument validation in base class
|
|||
} |
|||
|
|||
protected override MinimizationResult.ExitCondition WolfeExitCondition { get { return MinimizationResult.ExitCondition.StrongWolfeCriteria; } } |
|||
|
|||
protected override bool WolfeCondition(double stepDd, double initialDd) |
|||
{ |
|||
return Math.Abs(stepDd) > C2 * Math.Abs(initialDd); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,97 @@ |
|||
// <copyright file="WeakWolfeLineSearch.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-2016 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 MathNet.Numerics.LinearAlgebra; |
|||
|
|||
namespace MathNet.Numerics.Optimization.LineSearch |
|||
{ |
|||
/// <summary>
|
|||
/// Search for a step size alpha that satisfies the weak wolfe conditions. The weak Wolfe
|
|||
/// Conditions are
|
|||
/// i) Armijo Rule: f(x_k + alpha_k p_k) <= f(x_k) + c1 alpha_k p_k^T g(x_k)
|
|||
/// ii) Curvature Condition: p_k^T g(x_k + alpha_k p_k) >= c2 p_k^T g(x_k)
|
|||
/// where g(x) is the gradient of f(x), 0 < c1 < c2 < 1.
|
|||
///
|
|||
/// Implementation is based on http://www.math.washington.edu/~burke/crs/408/lectures/L9-weak-Wolfe.pdf
|
|||
///
|
|||
/// references:
|
|||
/// http://en.wikipedia.org/wiki/Wolfe_conditions
|
|||
/// http://www.math.washington.edu/~burke/crs/408/lectures/L9-weak-Wolfe.pdf
|
|||
/// </summary>
|
|||
public class WeakWolfeLineSearch : WolfeLineSearch |
|||
{ |
|||
public WeakWolfeLineSearch(double c1, double c2, double parameterTolerance, int maxIterations = 10) |
|||
: base(c1,c2,parameterTolerance,maxIterations) |
|||
{ |
|||
// Validation in base class
|
|||
} |
|||
|
|||
protected override MinimizationResult.ExitCondition WolfeExitCondition |
|||
{ |
|||
get { return MinimizationResult.ExitCondition.WeakWolfeCriteria; } |
|||
} |
|||
|
|||
protected override bool WolfeCondition(double stepDd, double initialDd) |
|||
{ |
|||
return stepDd < C2 * initialDd; |
|||
} |
|||
|
|||
protected override void ValidateValue(IObjectiveFunction eval) |
|||
{ |
|||
if (!IsFinite(eval.Value)) |
|||
{ |
|||
throw new EvaluationException(String.Format("Non-finite value returned by objective function: {0}", eval.Value), eval); |
|||
} |
|||
} |
|||
|
|||
protected override void ValidateInputArguments(IObjectiveFunctionEvaluation startingPoint, Vector<double> searchDirection, double initialStep, double upperBound) |
|||
{ |
|||
if (!startingPoint.IsGradientSupported) |
|||
throw new ArgumentException("objective function does not support gradient"); |
|||
} |
|||
|
|||
protected override void ValidateGradient(IObjectiveFunction eval) |
|||
{ |
|||
foreach (double x in eval.Gradient) |
|||
{ |
|||
if (!IsFinite(x)) |
|||
{ |
|||
throw new EvaluationException(string.Format("Non-finite value returned by gradient: {0}", x), eval); |
|||
} |
|||
} |
|||
} |
|||
|
|||
static bool IsFinite(double x) |
|||
{ |
|||
return !(double.IsNaN(x) || double.IsInfinity(x)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,158 @@ |
|||
// <copyright file="BfgsTest.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-2016 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 System; |
|||
|
|||
namespace MathNet.Numerics.Optimization.LineSearch |
|||
{ |
|||
public abstract class WolfeLineSearch |
|||
{ |
|||
protected double C1 { get; } |
|||
protected double C2 { get; } |
|||
protected double ParameterTolerance { get; } |
|||
protected int MaximumIterations { get; } |
|||
|
|||
public WolfeLineSearch(double c1, double c2, double parameterTolerance, int maxIterations = 10) |
|||
{ |
|||
if (c1 <= 0) |
|||
throw new ArgumentException(string.Format("c1 {0} should be greater than 0", c1)); |
|||
if (c2 <= c1) |
|||
throw new ArgumentException(string.Format("c1 {0} should be less than c2 {1}", c1, c2)); |
|||
if (c2 >= 1) |
|||
throw new ArgumentException(string.Format("c2 {0} should be less than 1", c2)); |
|||
|
|||
C1 = c1; |
|||
C2 = c2; |
|||
ParameterTolerance = parameterTolerance; |
|||
MaximumIterations = maxIterations; |
|||
} |
|||
|
|||
/// <summary>Implemented following http://www.math.washington.edu/~burke/crs/408/lectures/L9-weak-Wolfe.pdf</summary>
|
|||
/// <param name="startingPoint">The objective function being optimized, evaluated at the starting point of the search</param>
|
|||
/// <param name="searchDirection">Search direction</param>
|
|||
/// <param name="initialStep">Initial size of the step in the search direction</param>
|
|||
public LineSearchResult FindConformingStep(IObjectiveFunctionEvaluation startingPoint, Vector<double> searchDirection, double initialStep) |
|||
{ |
|||
return FindConformingStep(startingPoint, searchDirection, initialStep, double.PositiveInfinity); |
|||
} |
|||
|
|||
/// <summary></summary>
|
|||
/// <param name="startingPoint">The objective function being optimized, evaluated at the starting point of the search</param>
|
|||
/// <param name="searchDirection">Search direction</param>
|
|||
/// <param name="initialStep">Initial size of the step in the search direction</param>
|
|||
/// <param name="upperBound">The upper bound</param>
|
|||
public LineSearchResult FindConformingStep(IObjectiveFunctionEvaluation startingPoint, Vector<double> searchDirection, double initialStep, double upperBound) |
|||
{ |
|||
ValidateInputArguments(startingPoint, searchDirection, initialStep, upperBound); |
|||
|
|||
double lowerBound = 0.0; |
|||
double step = initialStep; |
|||
|
|||
double initialValue = startingPoint.Value; |
|||
Vector<double> initialGradient = startingPoint.Gradient; |
|||
|
|||
double initialDd = searchDirection * initialGradient; |
|||
|
|||
IObjectiveFunction objective = startingPoint.CreateNew(); |
|||
int ii; |
|||
MinimizationResult.ExitCondition reasonForExit = MinimizationResult.ExitCondition.None; |
|||
for (ii = 0; ii < MaximumIterations; ++ii) |
|||
{ |
|||
objective.EvaluateAt(startingPoint.Point + searchDirection * step); |
|||
ValidateGradient(objective); |
|||
ValidateValue(objective); |
|||
|
|||
double stepDd = searchDirection * objective.Gradient; |
|||
|
|||
if (objective.Value > initialValue + C1 * step * initialDd) |
|||
{ |
|||
upperBound = step; |
|||
step = 0.5 * (lowerBound + upperBound); |
|||
} |
|||
else if (WolfeCondition(stepDd,initialDd)) |
|||
{ |
|||
lowerBound = step; |
|||
step = double.IsPositiveInfinity(upperBound) ? 2 * lowerBound : 0.5 * (lowerBound + upperBound); |
|||
} |
|||
else |
|||
{ |
|||
reasonForExit = WolfeExitCondition; |
|||
break; |
|||
} |
|||
|
|||
if (!double.IsInfinity(upperBound)) |
|||
{ |
|||
double maxRelChange = 0.0; |
|||
for (int jj = 0; jj < objective.Point.Count; ++jj) |
|||
{ |
|||
double tmp = Math.Abs(searchDirection[jj] * (upperBound - lowerBound)) / Math.Max(Math.Abs(objective.Point[jj]), 1.0); |
|||
maxRelChange = Math.Max(maxRelChange, tmp); |
|||
} |
|||
if (maxRelChange < ParameterTolerance) |
|||
{ |
|||
reasonForExit = MinimizationResult.ExitCondition.LackOfProgress; |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (ii == MaximumIterations && Double.IsPositiveInfinity(upperBound)) |
|||
{ |
|||
throw new MaximumIterationsException(String.Format("Maximum iterations ({0}) reached. Function appears to be unbounded in search direction.", MaximumIterations)); |
|||
} |
|||
|
|||
if (ii == MaximumIterations) |
|||
{ |
|||
throw new MaximumIterationsException(String.Format("Maximum iterations ({0}) reached.", MaximumIterations)); |
|||
} |
|||
|
|||
return new LineSearchResult(objective, ii, step, reasonForExit); |
|||
} |
|||
protected abstract MinimizationResult.ExitCondition WolfeExitCondition { get; } |
|||
|
|||
protected abstract bool WolfeCondition(double stepDd, double initialDd); |
|||
|
|||
protected virtual void ValidateGradient(IObjectiveFunction objective) |
|||
{ |
|||
} |
|||
protected virtual void ValidateValue(IObjectiveFunction objective) |
|||
{ |
|||
} |
|||
|
|||
protected virtual void ValidateInputArguments(IObjectiveFunctionEvaluation startingPoint, Vector<double> searchDirection, double initialStep, double upperBound) |
|||
{ |
|||
|
|||
} |
|||
} |
|||
|
|||
|
|||
|
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
using MathNet.Numerics.LinearAlgebra; |
|||
|
|||
namespace MathNet.Numerics.Optimization |
|||
{ |
|||
public class MinimizationResult |
|||
{ |
|||
public enum ExitCondition |
|||
{ |
|||
None, |
|||
RelativeGradient, |
|||
LackOfProgress, |
|||
AbsoluteGradient, |
|||
WeakWolfeCriteria, |
|||
BoundTolerance, |
|||
StrongWolfeCriteria, |
|||
LackOfFunctionImprovement, |
|||
Converged |
|||
} |
|||
|
|||
public Vector<double> MinimizingPoint { get { return FunctionInfoAtMinimum.Point; } } |
|||
public IObjectiveFunction FunctionInfoAtMinimum { get; private set; } |
|||
public int Iterations { get; private set; } |
|||
public ExitCondition ReasonForExit { get; private set; } |
|||
|
|||
public MinimizationResult(IObjectiveFunction functionInfo, int iterations, ExitCondition reasonForExit) |
|||
{ |
|||
FunctionInfoAtMinimum = functionInfo; |
|||
Iterations = iterations; |
|||
ReasonForExit = reasonForExit; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
namespace MathNet.Numerics.Optimization |
|||
{ |
|||
public class MinimizationResult1D |
|||
{ |
|||
public double MinimizingPoint { get { return FunctionInfoAtMinimum.Point; } } |
|||
public IEvaluation1D FunctionInfoAtMinimum { get; private set; } |
|||
public int Iterations { get; private set; } |
|||
public MinimizationResult.ExitCondition ReasonForExit { get; private set; } |
|||
|
|||
public MinimizationResult1D(IEvaluation1D functionInfo, int iterations, MinimizationResult.ExitCondition reasonForExit) |
|||
{ |
|||
FunctionInfoAtMinimum = functionInfo; |
|||
Iterations = iterations; |
|||
ReasonForExit = reasonForExit; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
namespace MathNet.Numerics.Optimization |
|||
{ |
|||
public class MinimizationWithLineSearchResult : MinimizationResult |
|||
{ |
|||
public int TotalLineSearchIterations { get; private set; } |
|||
public int IterationsWithNonTrivialLineSearch { get; private set; } |
|||
|
|||
public MinimizationWithLineSearchResult(IObjectiveFunction functionInfo, int iterations, ExitCondition reasonForExit, int totalLineSearchIterations, int iterationsWithNonTrivialLineSearch) |
|||
: base(functionInfo, iterations, reasonForExit) |
|||
{ |
|||
TotalLineSearchIterations = totalLineSearchIterations; |
|||
IterationsWithNonTrivialLineSearch = iterationsWithNonTrivialLineSearch; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,415 @@ |
|||
// <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.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
|
|||
namespace MathNet.Numerics.Optimization |
|||
{ |
|||
/// <summary>
|
|||
/// Class implementing the Nelder-Mead simplex algorithm, used to find a minima when no gradient is available.
|
|||
/// Called fminsearch() in Matlab. A description of the algorithm can be found at
|
|||
/// http://se.mathworks.com/help/matlab/math/optimizing-nonlinear-functions.html#bsgpq6p-11
|
|||
/// or
|
|||
/// https://en.wikipedia.org/wiki/Nelder%E2%80%93Mead_method
|
|||
/// </summary>
|
|||
public sealed class NelderMeadSimplex |
|||
{ |
|||
private 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; } |
|||
|
|||
public NelderMeadSimplex(double convergenceTolerance, int maximumIterations) |
|||
{ |
|||
ConvergenceTolerance = convergenceTolerance; |
|||
MaximumIterations = maximumIterations; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 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
|
|||
/// </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) |
|||
{ |
|||
var initalPertubation = new MathNet.Numerics.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); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Finds the minimum of the objective function with an intial pertubation
|
|||
/// </summary>
|
|||
/// <param name="objectiveFunction">The objective function, no gradient or hessian needed</param>
|
|||
/// <param name="initialGuess">The intial guess</param>
|
|||
/// <param name="initalPertubation">The inital pertubation</param>
|
|||
/// <returns>The minimum point</returns>
|
|||
public MinimizationResult FindMinimum(IObjectiveFunction objectiveFunction, Vector<double> initialGuess, Vector<double> initalPertubation) |
|||
{ |
|||
// confirm that we are in a position to commence
|
|||
if (objectiveFunction == null) |
|||
throw new ArgumentNullException("objectiveFunction","ObjectiveFunction must be set to a valid ObjectiveFunctionDelegate"); |
|||
|
|||
if (initialGuess == null) |
|||
throw new ArgumentNullException("initialGuess", "initialGuess must be initialized"); |
|||
|
|||
if (initialGuess == null) |
|||
throw new ArgumentNullException("initalPertubation", "initalPertubation must be initialized, if unknown use overloaded version of FindMinimum()"); |
|||
|
|||
SimplexConstant[] simplexConstants = SimplexConstant.CreateSimplexConstantsFromVectors(initialGuess,initalPertubation); |
|||
|
|||
// create the initial simplex
|
|||
int numDimensions = simplexConstants.Length; |
|||
int numVertices = numDimensions + 1; |
|||
Vector<double>[] vertices = InitializeVertices(simplexConstants); |
|||
double[] errorValues = new double[numVertices]; |
|||
|
|||
int evaluationCount = 0; |
|||
MinimizationResult.ExitCondition exitCondition = MinimizationResult.ExitCondition.None; |
|||
ErrorProfile errorProfile; |
|||
|
|||
errorValues = InitializeErrorValues(vertices, objectiveFunction); |
|||
|
|||
// iterate until we converge, or complete our permitted number of iterations
|
|||
while (true) |
|||
{ |
|||
errorProfile = EvaluateSimplex(errorValues); |
|||
|
|||
// see if the range in point heights is small enough to exit
|
|||
if (HasConverged(ConvergenceTolerance, errorProfile, errorValues)) |
|||
{ |
|||
exitCondition = MinimizationResult.ExitCondition.Converged; |
|||
break; |
|||
} |
|||
|
|||
// attempt a reflection of the simplex
|
|||
double reflectionPointValue = TryToScaleSimplex(-1.0, ref errorProfile, vertices, errorValues, objectiveFunction); |
|||
++evaluationCount; |
|||
if (reflectionPointValue <= errorValues[errorProfile.LowestIndex]) |
|||
{ |
|||
// it's better than the best point, so attempt an expansion of the simplex
|
|||
double expansionPointValue = TryToScaleSimplex(2.0, ref errorProfile, vertices, errorValues, objectiveFunction); |
|||
++evaluationCount; |
|||
} |
|||
else if (reflectionPointValue >= errorValues[errorProfile.NextHighestIndex]) |
|||
{ |
|||
// it would be worse than the second best point, so attempt a contraction to look
|
|||
// for an intermediate point
|
|||
double currentWorst = errorValues[errorProfile.HighestIndex]; |
|||
double contractionPointValue = TryToScaleSimplex(0.5, ref errorProfile, vertices, errorValues, objectiveFunction); |
|||
++evaluationCount; |
|||
if (contractionPointValue >= currentWorst) |
|||
{ |
|||
// that would be even worse, so let's try to contract uniformly towards the low point;
|
|||
// don't bother to update the error profile, we'll do it at the start of the
|
|||
// next iteration
|
|||
ShrinkSimplex(errorProfile, vertices, errorValues, objectiveFunction); |
|||
evaluationCount += numVertices; // that required one function evaluation for each vertex; keep track
|
|||
} |
|||
} |
|||
// check to see if we have exceeded our alloted number of evaluations
|
|||
if (evaluationCount >= MaximumIterations) |
|||
{ |
|||
throw new MaximumIterationsException(String.Format("Maximum iterations ({0}) reached.", MaximumIterations)); |
|||
} |
|||
} |
|||
var regressionResult = new MinimizationResult(objectiveFunction, evaluationCount, exitCondition); |
|||
return regressionResult; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Evaluate the objective function at each vertex to create a corresponding
|
|||
/// list of error values for each vertex
|
|||
/// </summary>
|
|||
/// <param name="vertices"></param>
|
|||
/// <param name="objectiveFunction"></param>
|
|||
/// <returns></returns>
|
|||
private static double[] InitializeErrorValues(Vector<double>[] vertices, IObjectiveFunction objectiveFunction) |
|||
{ |
|||
double[] errorValues = new double[vertices.Length]; |
|||
for (int i = 0; i < vertices.Length; i++) |
|||
{ |
|||
objectiveFunction.EvaluateAt(vertices[i]); |
|||
errorValues[i] = objectiveFunction.Value; |
|||
} |
|||
return errorValues; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Check whether the points in the error profile have so little range that we
|
|||
/// consider ourselves to have converged
|
|||
/// </summary>
|
|||
/// <param name="convergenceTolerance"></param>
|
|||
/// <param name="errorProfile"></param>
|
|||
/// <param name="errorValues"></param>
|
|||
/// <returns></returns>
|
|||
private 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); |
|||
|
|||
if (range < convergenceTolerance) |
|||
{ |
|||
return true; |
|||
} |
|||
else |
|||
{ |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Examine all error values to determine the ErrorProfile
|
|||
/// </summary>
|
|||
/// <param name="errorValues"></param>
|
|||
/// <returns></returns>
|
|||
private static ErrorProfile EvaluateSimplex(double[] errorValues) |
|||
{ |
|||
ErrorProfile errorProfile = new ErrorProfile(); |
|||
if (errorValues[0] > errorValues[1]) |
|||
{ |
|||
errorProfile.HighestIndex = 0; |
|||
errorProfile.NextHighestIndex = 1; |
|||
} |
|||
else |
|||
{ |
|||
errorProfile.HighestIndex = 1; |
|||
errorProfile.NextHighestIndex = 0; |
|||
} |
|||
|
|||
for (int index = 0; index < errorValues.Length; index++) |
|||
{ |
|||
double errorValue = errorValues[index]; |
|||
if (errorValue <= errorValues[errorProfile.LowestIndex]) |
|||
{ |
|||
errorProfile.LowestIndex = index; |
|||
} |
|||
if (errorValue > errorValues[errorProfile.HighestIndex]) |
|||
{ |
|||
errorProfile.NextHighestIndex = errorProfile.HighestIndex; // downgrade the current highest to next highest
|
|||
errorProfile.HighestIndex = index; |
|||
} |
|||
else if (errorValue > errorValues[errorProfile.NextHighestIndex] && index != errorProfile.HighestIndex) |
|||
{ |
|||
errorProfile.NextHighestIndex = index; |
|||
} |
|||
} |
|||
|
|||
return errorProfile; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Construct an initial simplex, given starting guesses for the constants, and
|
|||
/// initial step sizes for each dimension
|
|||
/// </summary>
|
|||
/// <param name="simplexConstants"></param>
|
|||
/// <returns></returns>
|
|||
private static Vector<double>[] InitializeVertices(SimplexConstant[] simplexConstants) |
|||
{ |
|||
int numDimensions = simplexConstants.Length; |
|||
Vector<double>[] vertices = new Vector<double>[numDimensions + 1]; |
|||
|
|||
// define one point of the simplex as the given initial guesses
|
|||
var p0 = new MathNet.Numerics.LinearAlgebra.Double.DenseVector(numDimensions); |
|||
for (int i = 0; i < numDimensions; i++) |
|||
{ |
|||
p0[i] = simplexConstants[i].Value; |
|||
} |
|||
|
|||
// now fill in the vertices, creating the additional points as:
|
|||
// P(i) = P(0) + Scale(i) * UnitVector(i)
|
|||
vertices[0] = p0; |
|||
for (int i = 0; i < numDimensions; i++) |
|||
{ |
|||
double scale = simplexConstants[i].InitialPerturbation; |
|||
Vector<double> unitVector = new MathNet.Numerics.LinearAlgebra.Double.DenseVector(numDimensions); |
|||
unitVector[i] = 1; |
|||
vertices[i + 1] = p0.Add(unitVector.Multiply(scale)); |
|||
} |
|||
return vertices; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Test a scaling operation of the high point, and replace it if it is an improvement
|
|||
/// </summary>
|
|||
/// <param name="scaleFactor"></param>
|
|||
/// <param name="errorProfile"></param>
|
|||
/// <param name="vertices"></param>
|
|||
/// <param name="errorValues"></param>
|
|||
/// <param name="objectiveFunction"></param>
|
|||
/// <returns></returns>
|
|||
private static double TryToScaleSimplex(double scaleFactor, ref ErrorProfile errorProfile, Vector<double>[] vertices, |
|||
double[] errorValues, IObjectiveFunction objectiveFunction) |
|||
{ |
|||
// find the centroid through which we will reflect
|
|||
Vector<double> centroid = ComputeCentroid(vertices, errorProfile); |
|||
|
|||
// define the vector from the centroid to the high point
|
|||
Vector<double> centroidToHighPoint = vertices[errorProfile.HighestIndex].Subtract(centroid); |
|||
|
|||
// scale and position the vector to determine the new trial point
|
|||
Vector<double> newPoint = centroidToHighPoint.Multiply(scaleFactor).Add(centroid); |
|||
|
|||
// evaluate the new point
|
|||
objectiveFunction.EvaluateAt(newPoint); |
|||
double newErrorValue = objectiveFunction.Value; |
|||
|
|||
// if it's better, replace the old high point
|
|||
if (newErrorValue < errorValues[errorProfile.HighestIndex]) |
|||
{ |
|||
vertices[errorProfile.HighestIndex] = newPoint; |
|||
errorValues[errorProfile.HighestIndex] = newErrorValue; |
|||
} |
|||
|
|||
return newErrorValue; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Contract the simplex uniformly around the lowest point
|
|||
/// </summary>
|
|||
/// <param name="errorProfile"></param>
|
|||
/// <param name="vertices"></param>
|
|||
/// <param name="errorValues"></param>
|
|||
/// <param name="objectiveFunction"></param>
|
|||
private static void ShrinkSimplex(ErrorProfile errorProfile, Vector<double>[] vertices, double[] errorValues, |
|||
IObjectiveFunction objectiveFunction) |
|||
{ |
|||
Vector<double> lowestVertex = vertices[errorProfile.LowestIndex]; |
|||
for (int i = 0; i < vertices.Length; i++) |
|||
{ |
|||
if (i != errorProfile.LowestIndex) |
|||
{ |
|||
vertices[i] = (vertices[i].Add(lowestVertex)).Multiply(0.5); |
|||
objectiveFunction.EvaluateAt(vertices[i]); |
|||
errorValues[i] = objectiveFunction.Value; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Compute the centroid of all points except the worst
|
|||
/// </summary>
|
|||
/// <param name="vertices"></param>
|
|||
/// <param name="errorProfile"></param>
|
|||
/// <returns></returns>
|
|||
private static Vector<double> ComputeCentroid(Vector<double>[] vertices, ErrorProfile errorProfile) |
|||
{ |
|||
int numVertices = vertices.Length; |
|||
// find the centroid of all points except the worst one
|
|||
Vector<double> centroid = new MathNet.Numerics.LinearAlgebra.Double.DenseVector(numVertices - 1); |
|||
for (int i = 0; i < numVertices; i++) |
|||
{ |
|||
if (i != errorProfile.HighestIndex) |
|||
{ |
|||
centroid = centroid.Add(vertices[i]); |
|||
} |
|||
} |
|||
return centroid.Multiply(1.0d / (numVertices - 1)); |
|||
} |
|||
|
|||
private sealed class SimplexConstant |
|||
{ |
|||
private double _value; |
|||
private double _initialPerturbation; |
|||
|
|||
public SimplexConstant(double value, double initialPerturbation) |
|||
{ |
|||
_value = value; |
|||
_initialPerturbation = initialPerturbation; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// The value of the constant
|
|||
/// </summary>
|
|||
public double Value |
|||
{ |
|||
get { return _value; } |
|||
set { _value = value; } |
|||
} |
|||
|
|||
// The size of the initial perturbation
|
|||
public double InitialPerturbation |
|||
{ |
|||
get { return _initialPerturbation; } |
|||
set { _initialPerturbation = value; } |
|||
} |
|||
|
|||
public static SimplexConstant[] CreateSimplexConstantsFromVectors(Vector<double> initialGuess, Vector<double> initialPertubation) |
|||
{ |
|||
var constants = new SimplexConstant[initialGuess.Count]; |
|||
for (int i = 0; i < constants.Length;i++ ) |
|||
{ |
|||
constants[i] = new SimplexConstant(initialGuess[i], initialPertubation[i]); |
|||
} |
|||
return constants; |
|||
} |
|||
} |
|||
|
|||
private 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; } |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
|
|||
} |
|||
@ -0,0 +1,130 @@ |
|||
using System; |
|||
using MathNet.Numerics.LinearAlgebra; |
|||
using MathNet.Numerics.Optimization.LineSearch; |
|||
|
|||
namespace MathNet.Numerics.Optimization |
|||
{ |
|||
public class NewtonMinimizer |
|||
{ |
|||
public double GradientTolerance { get; set; } |
|||
public int MaximumIterations { get; set; } |
|||
public bool UseLineSearch { get; set; } |
|||
|
|||
public NewtonMinimizer(double gradientTolerance, int maximumIterations, bool useLineSearch = false) |
|||
{ |
|||
GradientTolerance = gradientTolerance; |
|||
MaximumIterations = maximumIterations; |
|||
UseLineSearch = useLineSearch; |
|||
} |
|||
|
|||
public MinimizationResult FindMinimum(IObjectiveFunction objective, Vector<double> initialGuess) |
|||
{ |
|||
if (!objective.IsGradientSupported) |
|||
{ |
|||
throw new IncompatibleObjectiveException("Gradient not supported in objective function, but required for Newton minimization."); |
|||
} |
|||
|
|||
if (!objective.IsHessianSupported) |
|||
{ |
|||
throw new IncompatibleObjectiveException("Hessian not supported in objective function, but required for Newton minimization."); |
|||
} |
|||
|
|||
// Check that we're not already done
|
|||
objective.EvaluateAt(initialGuess); |
|||
ValidateGradient(objective); |
|||
if (ExitCriteriaSatisfied(objective.Gradient)) |
|||
{ |
|||
return new MinimizationResult(objective, 0, MinimizationResult.ExitCondition.AbsoluteGradient); |
|||
} |
|||
|
|||
// Set up line search algorithm
|
|||
var lineSearcher = new WeakWolfeLineSearch(1e-4, 0.9, 1e-4, maxIterations: 1000); |
|||
|
|||
// Subsequent steps
|
|||
int iterations = 0; |
|||
int totalLineSearchSteps = 0; |
|||
int iterationsWithNontrivialLineSearch = 0; |
|||
bool tmpLineSearch = false; |
|||
while (!ExitCriteriaSatisfied(objective.Gradient) && iterations < MaximumIterations) |
|||
{ |
|||
ValidateHessian(objective); |
|||
|
|||
var searchDirection = objective.Hessian.LU().Solve(-objective.Gradient); |
|||
if (searchDirection * objective.Gradient >= 0) |
|||
{ |
|||
searchDirection = -objective.Gradient; |
|||
tmpLineSearch = true; |
|||
} |
|||
|
|||
if (UseLineSearch || tmpLineSearch) |
|||
{ |
|||
LineSearchResult result; |
|||
try |
|||
{ |
|||
result = lineSearcher.FindConformingStep(objective, searchDirection, 1.0); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
throw new InnerOptimizationException("Line search failed.", e); |
|||
} |
|||
|
|||
iterationsWithNontrivialLineSearch += result.Iterations > 0 ? 1 : 0; |
|||
totalLineSearchSteps += result.Iterations; |
|||
objective = result.FunctionInfoAtMinimum; |
|||
} |
|||
else |
|||
{ |
|||
objective.EvaluateAt(objective.Point + searchDirection); |
|||
} |
|||
|
|||
ValidateGradient(objective); |
|||
|
|||
tmpLineSearch = false; |
|||
iterations += 1; |
|||
} |
|||
|
|||
if (iterations == MaximumIterations) |
|||
{ |
|||
throw new MaximumIterationsException(String.Format("Maximum iterations ({0}) reached.", MaximumIterations)); |
|||
} |
|||
|
|||
return new MinimizationWithLineSearchResult(objective, iterations, MinimizationResult.ExitCondition.AbsoluteGradient, totalLineSearchSteps, iterationsWithNontrivialLineSearch); |
|||
} |
|||
|
|||
bool ExitCriteriaSatisfied(Vector<double> gradient) |
|||
{ |
|||
return gradient.Norm(2.0) < GradientTolerance; |
|||
} |
|||
|
|||
static void ValidateGradient(IObjectiveFunction eval) |
|||
{ |
|||
foreach (var x in eval.Gradient) |
|||
{ |
|||
if (Double.IsNaN(x) || Double.IsInfinity(x)) |
|||
{ |
|||
throw new EvaluationException("Non-finite gradient returned.", eval); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private void ValidateObjective(IObjectiveFunction eval) |
|||
{ |
|||
if (Double.IsNaN(eval.Value) || Double.IsInfinity(eval.Value)) |
|||
throw new EvaluationException("Non-finite objective function returned.", eval); |
|||
} |
|||
|
|||
private void ValidateHessian(IObjectiveFunction eval) |
|||
{ |
|||
for (int ii = 0; ii < eval.Hessian.RowCount; ++ii) |
|||
{ |
|||
for (int jj = 0; jj < eval.Hessian.ColumnCount; ++jj) |
|||
{ |
|||
if (Double.IsNaN(eval.Hessian[ii, jj]) || Double.IsInfinity(eval.Hessian[ii, jj])) |
|||
{ |
|||
throw new EvaluationException("Non-finite Hessian returned.", eval); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,65 @@ |
|||
using System; |
|||
using MathNet.Numerics.LinearAlgebra; |
|||
using MathNet.Numerics.Optimization.ObjectiveFunctions; |
|||
|
|||
namespace MathNet.Numerics.Optimization |
|||
{ |
|||
public static class ObjectiveFunction |
|||
{ |
|||
/// <summary>
|
|||
/// Objective function where neither Gradient nor Hessian is available.
|
|||
/// </summary>
|
|||
public static IObjectiveFunction Value(Func<Vector<double>, double> function) |
|||
{ |
|||
return new ValueObjectiveFunction(function); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Objective function where the Gradient is available. Greedy evaluation.
|
|||
/// </summary>
|
|||
public static IObjectiveFunction Gradient(Func<Vector<double>, Tuple<double, Vector<double>>> function) |
|||
{ |
|||
return new GradientObjectiveFunction(function); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Objective function where the Gradient is available. Lazy evaluation.
|
|||
/// </summary>
|
|||
public static IObjectiveFunction Gradient(Func<Vector<double>, double> function, Func<Vector<double>, Vector<double>> gradient) |
|||
{ |
|||
return new LazyObjectiveFunction(function, gradient: gradient); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Objective function where the Hessian is available. Greedy evaluation.
|
|||
/// </summary>
|
|||
public static IObjectiveFunction Hessian(Func<Vector<double>, Tuple<double, Matrix<double>>> function) |
|||
{ |
|||
return new HessianObjectiveFunction(function); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Objective function where the Hessian is available. Lazy evaluation.
|
|||
/// </summary>
|
|||
public static IObjectiveFunction Hessian(Func<Vector<double>, double> function, Func<Vector<double>, Matrix<double>> hessian) |
|||
{ |
|||
return new LazyObjectiveFunction(function, hessian: hessian); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Objective function where both Gradient and Hessian are available. Greedy evaluation.
|
|||
/// </summary>
|
|||
public static IObjectiveFunction GradientHessian(Func<Vector<double>, Tuple<double, Vector<double>, Matrix<double>>> function) |
|||
{ |
|||
return new GradientHessianObjectiveFunction(function); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Objective function where both Gradient and Hessian are available. Lazy evaluation.
|
|||
/// </summary>
|
|||
public static IObjectiveFunction GradientHessian(Func<Vector<double>, double> function, Func<Vector<double>, Vector<double>> gradient, Func<Vector<double>, Matrix<double>> hessian) |
|||
{ |
|||
return new LazyObjectiveFunction(function, gradient: gradient, hessian: hessian); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,98 @@ |
|||
using System; |
|||
|
|||
namespace MathNet.Numerics.Optimization |
|||
{ |
|||
public interface IEvaluation1D |
|||
{ |
|||
double Point { get; } |
|||
double Value { get; } |
|||
double Derivative { get; } |
|||
double SecondDerivative { get; } |
|||
} |
|||
|
|||
public interface IObjectiveFunction1D |
|||
{ |
|||
bool DerivativeSupported { get; } |
|||
bool SecondDerivativeSupported { get; } |
|||
IEvaluation1D Evaluate(double point); |
|||
} |
|||
|
|||
public class CachedEvaluation1D : IEvaluation1D |
|||
{ |
|||
private double? _value; |
|||
private double? _derivative; |
|||
private double? _secondDerivative; |
|||
private readonly SimpleObjectiveFunction1D _objectiveObject; |
|||
private readonly double _point; |
|||
|
|||
public CachedEvaluation1D(SimpleObjectiveFunction1D f, double point) |
|||
{ |
|||
_objectiveObject = f; |
|||
_point = point; |
|||
} |
|||
private double SetValue() |
|||
{ |
|||
_value = _objectiveObject.Objective(_point); |
|||
return _value.Value; |
|||
} |
|||
private double SetDerivative() |
|||
{ |
|||
_derivative = _objectiveObject.Derivative(_point); |
|||
return _derivative.Value; |
|||
} |
|||
private double SetSecondDerivative() |
|||
{ |
|||
_secondDerivative = _objectiveObject.SecondDerivative(_point); |
|||
return _secondDerivative.Value; |
|||
} |
|||
|
|||
public double Point { get { return _point; } } |
|||
public double Value { get { return _value ?? SetValue(); } } |
|||
public double Derivative { get { return _derivative ?? SetDerivative(); } } |
|||
public double SecondDerivative { get { return _secondDerivative ?? SetSecondDerivative(); } } |
|||
|
|||
} |
|||
|
|||
public class SimpleObjectiveFunction1D : IObjectiveFunction1D |
|||
{ |
|||
public Func<double, double> Objective { get; private set; } |
|||
public Func<double, double> Derivative { get; private set; } |
|||
public Func<double, double> SecondDerivative { get; private set; } |
|||
|
|||
public SimpleObjectiveFunction1D(Func<double, double> objective) |
|||
{ |
|||
Objective = objective; |
|||
Derivative = null; |
|||
SecondDerivative = null; |
|||
} |
|||
|
|||
public SimpleObjectiveFunction1D(Func<double, double> objective, Func<double, double> derivative) |
|||
{ |
|||
Objective = objective; |
|||
Derivative = derivative; |
|||
SecondDerivative = null; |
|||
} |
|||
|
|||
public SimpleObjectiveFunction1D(Func<double, double> objective, Func<double, double> derivative, Func<double,double> secondDerivative) |
|||
{ |
|||
Objective = objective; |
|||
Derivative = derivative; |
|||
SecondDerivative = secondDerivative; |
|||
} |
|||
|
|||
public bool DerivativeSupported |
|||
{ |
|||
get { return Derivative != null; } |
|||
} |
|||
|
|||
public bool SecondDerivativeSupported |
|||
{ |
|||
get { return SecondDerivative != null; } |
|||
} |
|||
|
|||
public IEvaluation1D Evaluate(double point) |
|||
{ |
|||
return new CachedEvaluation1D(this, point); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,145 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using MathNet.Numerics.LinearAlgebra; |
|||
|
|||
namespace MathNet.Numerics.Optimization.ObjectiveFunctions |
|||
{ |
|||
/// <summary>
|
|||
/// Adapts an objective function with only value implemented
|
|||
/// to provide a gradient as well. Gradient calculation is
|
|||
/// done using the finite difference method, specifically
|
|||
/// forward differences.
|
|||
///
|
|||
/// For each gradient computed, the algorithm requires an
|
|||
/// additional number of function evaluations equal to the
|
|||
/// functions's number of input parameters.
|
|||
/// </summary>
|
|||
public class ForwardDifferenceGradientObjectiveFunction : IObjectiveFunction |
|||
{ |
|||
public IObjectiveFunction InnerObjectiveFunction { get; protected set; } |
|||
protected Vector<double> LowerBound { get; set; } |
|||
protected Vector<double> UpperBound { get; set; } |
|||
|
|||
protected bool ValueEvaluated { get; set; } = false; |
|||
protected bool GradientEvaluated { get; set; } = false; |
|||
private Vector<double> _gradient; |
|||
|
|||
public double MinimumIncrement { get; set; } |
|||
public double RelativeIncrement { get; set; } |
|||
|
|||
public ForwardDifferenceGradientObjectiveFunction(IObjectiveFunction valueOnlyObj, Vector<double> lowerBound, Vector<double> upperBound, double relativeIncrement=1e-5, double minimumIncrement=1e-8) |
|||
{ |
|||
InnerObjectiveFunction = valueOnlyObj; |
|||
LowerBound = lowerBound; |
|||
UpperBound = upperBound; |
|||
_gradient = new LinearAlgebra.Double.DenseVector(LowerBound.Count); |
|||
RelativeIncrement = relativeIncrement; |
|||
MinimumIncrement = minimumIncrement; |
|||
} |
|||
|
|||
protected void EvaluateValue() |
|||
{ |
|||
ValueEvaluated = true; |
|||
} |
|||
|
|||
protected void EvaluateGradient() |
|||
{ |
|||
if (!ValueEvaluated) |
|||
EvaluateValue(); |
|||
|
|||
var tmp_point = Point.Clone(); |
|||
var tmp_obj = InnerObjectiveFunction.CreateNew(); |
|||
for (int ii = 0; ii < _gradient.Count; ++ii) |
|||
{ |
|||
var orig_point = tmp_point[ii]; |
|||
var rel_incr = orig_point * RelativeIncrement; |
|||
var h = Math.Max(rel_incr, MinimumIncrement); |
|||
var mult = 1; |
|||
if (orig_point + h > UpperBound[ii]) |
|||
mult = -1; |
|||
|
|||
tmp_point[ii] = orig_point + mult*h; |
|||
tmp_obj.EvaluateAt(tmp_point); |
|||
double bumped_value = tmp_obj.Value; |
|||
_gradient[ii] = (mult * bumped_value - mult * InnerObjectiveFunction.Value) / h; |
|||
|
|||
tmp_point[ii] = orig_point; |
|||
} |
|||
GradientEvaluated = true; |
|||
} |
|||
|
|||
public Vector<double> Gradient |
|||
{ |
|||
get |
|||
{ |
|||
if (!GradientEvaluated) |
|||
EvaluateGradient(); |
|||
return _gradient; |
|||
} |
|||
protected set { _gradient = value; } |
|||
} |
|||
|
|||
public Matrix<double> Hessian |
|||
{ |
|||
get |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
} |
|||
|
|||
public bool IsGradientSupported |
|||
{ |
|||
get |
|||
{ |
|||
return true; |
|||
} |
|||
} |
|||
|
|||
public bool IsHessianSupported |
|||
{ |
|||
get |
|||
{ |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
public Vector<double> Point { get; protected set; } |
|||
|
|||
public double Value |
|||
{ |
|||
get |
|||
{ |
|||
if (!ValueEvaluated) |
|||
EvaluateValue(); |
|||
return this.InnerObjectiveFunction.Value; |
|||
} |
|||
} |
|||
|
|||
public IObjectiveFunction CreateNew() |
|||
{ |
|||
var tmp = new ForwardDifferenceGradientObjectiveFunction(this.InnerObjectiveFunction.CreateNew(), LowerBound, UpperBound, this.RelativeIncrement, this.MinimumIncrement); |
|||
return tmp; |
|||
} |
|||
|
|||
public void EvaluateAt(Vector<double> point) |
|||
{ |
|||
Point = point; |
|||
ValueEvaluated = false; |
|||
GradientEvaluated = false; |
|||
InnerObjectiveFunction.EvaluateAt(point); |
|||
} |
|||
|
|||
public IObjectiveFunction Fork() |
|||
{ |
|||
return new ForwardDifferenceGradientObjectiveFunction(this.InnerObjectiveFunction.Fork(), LowerBound, UpperBound, this.RelativeIncrement, this.MinimumIncrement) |
|||
{ |
|||
Point = Point?.Clone(), |
|||
GradientEvaluated = GradientEvaluated, |
|||
ValueEvaluated = ValueEvaluated, |
|||
_gradient = _gradient?.Clone() |
|||
}; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,57 @@ |
|||
using System; |
|||
using MathNet.Numerics.LinearAlgebra; |
|||
|
|||
namespace MathNet.Numerics.Optimization.ObjectiveFunctions |
|||
{ |
|||
internal class GradientHessianObjectiveFunction : IObjectiveFunction |
|||
{ |
|||
readonly Func<Vector<double>, Tuple<double, Vector<double>, Matrix<double>>> _function; |
|||
|
|||
public GradientHessianObjectiveFunction(Func<Vector<double>, Tuple<double, Vector<double>, Matrix<double>>> function) |
|||
{ |
|||
_function = function; |
|||
} |
|||
|
|||
public IObjectiveFunction CreateNew() |
|||
{ |
|||
return new GradientHessianObjectiveFunction(_function); |
|||
} |
|||
|
|||
public IObjectiveFunction Fork() |
|||
{ |
|||
// no need to deep-clone values since they are replaced on evaluation
|
|||
return new GradientHessianObjectiveFunction(_function) |
|||
{ |
|||
Point = Point, |
|||
Value = Value, |
|||
Gradient = Gradient, |
|||
Hessian = Hessian |
|||
}; |
|||
} |
|||
|
|||
public bool IsGradientSupported |
|||
{ |
|||
get { return true; } |
|||
} |
|||
|
|||
public bool IsHessianSupported |
|||
{ |
|||
get { return true; } |
|||
} |
|||
|
|||
public void EvaluateAt(Vector<double> point) |
|||
{ |
|||
Point = point; |
|||
|
|||
var result = _function(point); |
|||
Value = result.Item1; |
|||
Gradient = result.Item2; |
|||
Hessian = result.Item3; |
|||
} |
|||
|
|||
public Vector<double> Point { get; private set; } |
|||
public double Value { get; private set; } |
|||
public Vector<double> Gradient { get; private set; } |
|||
public Matrix<double> Hessian { get; private set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,59 @@ |
|||
using System; |
|||
using MathNet.Numerics.LinearAlgebra; |
|||
|
|||
namespace MathNet.Numerics.Optimization.ObjectiveFunctions |
|||
{ |
|||
internal class GradientObjectiveFunction : IObjectiveFunction |
|||
{ |
|||
readonly Func<Vector<double>, Tuple<double, Vector<double>>> _function; |
|||
|
|||
public GradientObjectiveFunction(Func<Vector<double>, Tuple<double, Vector<double>>> function) |
|||
{ |
|||
_function = function; |
|||
} |
|||
|
|||
public IObjectiveFunction CreateNew() |
|||
{ |
|||
return new GradientObjectiveFunction(_function); |
|||
} |
|||
|
|||
public IObjectiveFunction Fork() |
|||
{ |
|||
// no need to deep-clone values since they are replaced on evaluation
|
|||
return new GradientObjectiveFunction(_function) |
|||
{ |
|||
Point = Point, |
|||
Value = Value, |
|||
Gradient = Gradient |
|||
}; |
|||
} |
|||
|
|||
public bool IsGradientSupported |
|||
{ |
|||
get { return true; } |
|||
} |
|||
|
|||
public bool IsHessianSupported |
|||
{ |
|||
get { return false; } |
|||
} |
|||
|
|||
public void EvaluateAt(Vector<double> point) |
|||
{ |
|||
Point = point; |
|||
|
|||
var result = _function(point); |
|||
Value = result.Item1; |
|||
Gradient = result.Item2; |
|||
} |
|||
|
|||
public Vector<double> Point { get; private set; } |
|||
public double Value { get; private set; } |
|||
public Vector<double> Gradient { get; private set; } |
|||
|
|||
public Matrix<double> Hessian |
|||
{ |
|||
get { throw new NotSupportedException(); } |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,59 @@ |
|||
using System; |
|||
using MathNet.Numerics.LinearAlgebra; |
|||
|
|||
namespace MathNet.Numerics.Optimization.ObjectiveFunctions |
|||
{ |
|||
internal class HessianObjectiveFunction : IObjectiveFunction |
|||
{ |
|||
readonly Func<Vector<double>, Tuple<double, Matrix<double>>> _function; |
|||
|
|||
public HessianObjectiveFunction(Func<Vector<double>, Tuple<double, Matrix<double>>> function) |
|||
{ |
|||
_function = function; |
|||
} |
|||
|
|||
public IObjectiveFunction CreateNew() |
|||
{ |
|||
return new HessianObjectiveFunction(_function); |
|||
} |
|||
|
|||
public IObjectiveFunction Fork() |
|||
{ |
|||
// no need to deep-clone values since they are replaced on evaluation
|
|||
return new HessianObjectiveFunction(_function) |
|||
{ |
|||
Point = Point, |
|||
Value = Value, |
|||
Hessian = Hessian |
|||
}; |
|||
} |
|||
|
|||
public bool IsGradientSupported |
|||
{ |
|||
get { return false; } |
|||
} |
|||
|
|||
public bool IsHessianSupported |
|||
{ |
|||
get { return true; } |
|||
} |
|||
|
|||
public void EvaluateAt(Vector<double> point) |
|||
{ |
|||
Point = point; |
|||
|
|||
var result = _function(point); |
|||
Value = result.Item1; |
|||
Hessian = result.Item2; |
|||
} |
|||
|
|||
public Vector<double> Point { get; private set; } |
|||
public double Value { get; private set; } |
|||
public Matrix<double> Hessian { get; private set; } |
|||
|
|||
public Vector<double> Gradient |
|||
{ |
|||
get { throw new NotSupportedException(); } |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,142 @@ |
|||
// <copyright file="LazyObjectiveFunction.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-2016 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 MathNet.Numerics.LinearAlgebra; |
|||
|
|||
namespace MathNet.Numerics.Optimization.ObjectiveFunctions |
|||
{ |
|||
internal class LazyObjectiveFunction : IObjectiveFunction |
|||
{ |
|||
readonly Func<Vector<double>, double> _function; |
|||
readonly Func<Vector<double>, Vector<double>> _gradient; |
|||
readonly Func<Vector<double>, Matrix<double>> _hessian; |
|||
|
|||
Vector<double> _point; |
|||
|
|||
bool _hasFunctionValue; |
|||
double _functionValue; |
|||
|
|||
bool _hasGradientValue; |
|||
Vector<double> _gradientValue; |
|||
|
|||
bool _hasHessianValue; |
|||
Matrix<double> _hessianValue; |
|||
|
|||
public LazyObjectiveFunction(Func<Vector<double>, double> function, Func<Vector<double>, Vector<double>> gradient = null, Func<Vector<double>, Matrix<double>> hessian = null) |
|||
{ |
|||
_function = function; |
|||
_gradient = gradient; |
|||
_hessian = hessian; |
|||
|
|||
IsGradientSupported = gradient != null; |
|||
IsHessianSupported = hessian != null; |
|||
} |
|||
|
|||
public IObjectiveFunction CreateNew() |
|||
{ |
|||
return new LazyObjectiveFunction(_function, _gradient, _hessian); |
|||
} |
|||
|
|||
public IObjectiveFunction Fork() |
|||
{ |
|||
// no need to deep-clone values since they are replaced on evaluation
|
|||
return new LazyObjectiveFunction(_function, _gradient, _hessian) |
|||
{ |
|||
_point = _point, |
|||
_hasFunctionValue = _hasFunctionValue, |
|||
_functionValue = _functionValue, |
|||
_hasGradientValue = _hasGradientValue, |
|||
_gradientValue = _gradientValue, |
|||
_hasHessianValue = _hasHessianValue, |
|||
_hessianValue = _hessianValue |
|||
}; |
|||
} |
|||
|
|||
public bool IsGradientSupported { get; private set; } |
|||
public bool IsHessianSupported { get; private set; } |
|||
|
|||
public void EvaluateAt(Vector<double> point) |
|||
{ |
|||
_point = point; |
|||
_hasFunctionValue = false; |
|||
_hasGradientValue = false; |
|||
_hasHessianValue = false; |
|||
|
|||
// don't keep references unnecessarily
|
|||
_gradientValue = null; |
|||
_hessianValue = null; |
|||
} |
|||
|
|||
public Vector<double> Point |
|||
{ |
|||
get { return _point; } |
|||
} |
|||
|
|||
public double Value |
|||
{ |
|||
get |
|||
{ |
|||
if (!_hasFunctionValue) |
|||
{ |
|||
_functionValue = _function(_point); |
|||
_hasFunctionValue = true; |
|||
} |
|||
return _functionValue; |
|||
} |
|||
} |
|||
|
|||
public Vector<double> Gradient |
|||
{ |
|||
get |
|||
{ |
|||
if (!_hasGradientValue) |
|||
{ |
|||
_gradientValue = _gradient(_point); |
|||
_hasGradientValue = true; |
|||
} |
|||
return _gradientValue; |
|||
} |
|||
} |
|||
|
|||
public Matrix<double> Hessian |
|||
{ |
|||
get |
|||
{ |
|||
if (!_hasHessianValue) |
|||
{ |
|||
_hessianValue = _hessian(_point); |
|||
_hasHessianValue = true; |
|||
} |
|||
return _hessianValue; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,149 @@ |
|||
// <copyright file="LazyObjectiveFunctionBase.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-2016 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; |
|||
|
|||
namespace MathNet.Numerics.Optimization.ObjectiveFunctions |
|||
{ |
|||
public abstract class LazyObjectiveFunctionBase : IObjectiveFunction |
|||
{ |
|||
Vector<double> _point; |
|||
|
|||
protected bool HasFunctionValue { get; set; } |
|||
protected double FunctionValue { get; set; } |
|||
|
|||
protected bool HasGradientValue { get; set; } |
|||
protected Vector<double> GradientValue { get; set; } |
|||
|
|||
protected bool HasHessianValue { get; set; } |
|||
protected Matrix<double> HessianValue { get; set; } |
|||
|
|||
protected LazyObjectiveFunctionBase(bool gradientSupported, bool hessianSupported) |
|||
{ |
|||
IsGradientSupported = gradientSupported; |
|||
IsHessianSupported = hessianSupported; |
|||
} |
|||
|
|||
public abstract IObjectiveFunction CreateNew(); |
|||
|
|||
public virtual IObjectiveFunction Fork() |
|||
{ |
|||
// we need to deep-clone values since they may be updated inplace on evaluation
|
|||
LazyObjectiveFunctionBase fork = (LazyObjectiveFunctionBase)CreateNew(); |
|||
fork._point = _point?.Clone(); |
|||
fork.HasFunctionValue = HasFunctionValue; |
|||
fork.FunctionValue = FunctionValue; |
|||
fork.HasGradientValue = HasGradientValue; |
|||
fork.GradientValue = GradientValue?.Clone(); |
|||
fork.HasHessianValue = HasHessianValue; |
|||
fork.HessianValue = HessianValue?.Clone(); |
|||
return fork; |
|||
} |
|||
|
|||
public bool IsGradientSupported { get; private set; } |
|||
public bool IsHessianSupported { get; private set; } |
|||
|
|||
public void EvaluateAt(Vector<double> point) |
|||
{ |
|||
_point = point; |
|||
HasFunctionValue = false; |
|||
HasGradientValue = false; |
|||
HasHessianValue = false; |
|||
} |
|||
|
|||
protected abstract void EvaluateValue(); |
|||
|
|||
protected virtual void EvaluateGradient() |
|||
{ |
|||
Gradient = null; |
|||
} |
|||
|
|||
protected virtual void EvaluateHessian() |
|||
{ |
|||
Hessian = null; |
|||
} |
|||
|
|||
public Vector<double> Point |
|||
{ |
|||
get { return _point; } |
|||
} |
|||
|
|||
public double Value |
|||
{ |
|||
get |
|||
{ |
|||
if (!HasFunctionValue) |
|||
{ |
|||
EvaluateValue(); |
|||
} |
|||
return FunctionValue; |
|||
} |
|||
protected set |
|||
{ |
|||
FunctionValue = value; |
|||
HasFunctionValue = true; |
|||
} |
|||
} |
|||
|
|||
public Vector<double> Gradient |
|||
{ |
|||
get |
|||
{ |
|||
if (!HasGradientValue) |
|||
{ |
|||
EvaluateGradient(); |
|||
} |
|||
return GradientValue; |
|||
} |
|||
protected set |
|||
{ |
|||
GradientValue = value; |
|||
HasGradientValue = true; |
|||
} |
|||
} |
|||
|
|||
public Matrix<double> Hessian |
|||
{ |
|||
get |
|||
{ |
|||
if (!HasHessianValue) |
|||
{ |
|||
EvaluateHessian(); |
|||
} |
|||
return HessianValue; |
|||
} |
|||
protected set |
|||
{ |
|||
HessianValue = value; |
|||
HasHessianValue = true; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,42 @@ |
|||
using MathNet.Numerics.LinearAlgebra; |
|||
|
|||
namespace MathNet.Numerics.Optimization.ObjectiveFunctions |
|||
{ |
|||
public abstract class ObjectiveFunctionBase : IObjectiveFunction |
|||
{ |
|||
protected ObjectiveFunctionBase(bool isGradientSupported, bool isHessianSupported) |
|||
{ |
|||
IsGradientSupported = isGradientSupported; |
|||
IsHessianSupported = isHessianSupported; |
|||
} |
|||
|
|||
public abstract IObjectiveFunction CreateNew(); |
|||
|
|||
public virtual IObjectiveFunction Fork() |
|||
{ |
|||
// we need to deep-clone values since they may be updated inplace on evaluation
|
|||
ObjectiveFunctionBase objective = (ObjectiveFunctionBase)CreateNew(); |
|||
objective.Point = Point == null ? null : Point.Clone(); |
|||
objective.Value = Value; |
|||
objective.Gradient = Gradient == null ? null : Gradient.Clone(); |
|||
objective.Hessian = Hessian == null ? null : Hessian.Clone(); |
|||
return objective; |
|||
} |
|||
|
|||
public bool IsGradientSupported { get; private set; } |
|||
public bool IsHessianSupported { get; private set; } |
|||
|
|||
public void EvaluateAt(Vector<double> point) |
|||
{ |
|||
Point = point; |
|||
Evaluate(); |
|||
} |
|||
|
|||
protected abstract void Evaluate(); |
|||
|
|||
public Vector<double> Point { get; private set; } |
|||
public double Value { get; protected set; } |
|||
public Vector<double> Gradient { get; protected set; } |
|||
public Matrix<double> Hessian { get; protected set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,59 @@ |
|||
using System; |
|||
using MathNet.Numerics.LinearAlgebra; |
|||
|
|||
namespace MathNet.Numerics.Optimization.ObjectiveFunctions |
|||
{ |
|||
internal class ValueObjectiveFunction : IObjectiveFunction |
|||
{ |
|||
readonly Func<Vector<double>, double> _function; |
|||
|
|||
public ValueObjectiveFunction(Func<Vector<double>, double> function) |
|||
{ |
|||
_function = function; |
|||
} |
|||
|
|||
public IObjectiveFunction CreateNew() |
|||
{ |
|||
return new ValueObjectiveFunction(_function); |
|||
} |
|||
|
|||
public IObjectiveFunction Fork() |
|||
{ |
|||
// no need to deep-clone values since they are replaced on evaluation
|
|||
return new ValueObjectiveFunction(_function) |
|||
{ |
|||
Point = Point, |
|||
Value = Value, |
|||
}; |
|||
} |
|||
|
|||
public bool IsGradientSupported |
|||
{ |
|||
get { return false; } |
|||
} |
|||
|
|||
public bool IsHessianSupported |
|||
{ |
|||
get { return false; } |
|||
} |
|||
|
|||
public void EvaluateAt(Vector<double> point) |
|||
{ |
|||
Point = point; |
|||
Value = _function(point); |
|||
} |
|||
|
|||
public Vector<double> Point { get; private set; } |
|||
public double Value { get; private set; } |
|||
|
|||
public Matrix<double> Hessian |
|||
{ |
|||
get { throw new NotSupportedException(); } |
|||
} |
|||
|
|||
public Vector<double> Gradient |
|||
{ |
|||
get { throw new NotSupportedException(); } |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
|
|||
namespace MathNet.Numerics.Optimization |
|||
{ |
|||
} |
|||
@ -0,0 +1,99 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using MathNet.Numerics.LinearAlgebra; |
|||
|
|||
namespace MathNet.Numerics.Optimization |
|||
{ |
|||
public static class QuadraticGradientProjectionSearch |
|||
{ |
|||
public static GradientProjectionResult Search(Vector<double> x0, Vector<double> gradient, Matrix<double> hessian, Vector<double> lowerBound, Vector<double> upperBound) |
|||
{ |
|||
List<bool> isFixed = new List<bool>(x0.Count); |
|||
List<double> breakpoint = new List<double>(x0.Count); |
|||
for (int ii = 0; ii < x0.Count; ++ii) |
|||
{ |
|||
breakpoint.Add(0.0); |
|||
isFixed.Add(false); |
|||
if (gradient[ii] < 0) |
|||
breakpoint[ii] = (x0[ii] - upperBound[ii]) / gradient[ii]; |
|||
else if (gradient[ii] > 0) |
|||
breakpoint[ii] = (x0[ii] - lowerBound[ii]) / gradient[ii]; |
|||
else |
|||
{ |
|||
if (Math.Abs(x0[ii] - upperBound[ii]) < 100 * Double.Epsilon || Math.Abs(x0[ii] - lowerBound[ii]) < 100 * Double.Epsilon) |
|||
breakpoint[ii] = 0.0; |
|||
else |
|||
breakpoint[ii] = Double.PositiveInfinity; |
|||
} |
|||
} |
|||
|
|||
var orderedBreakpoint = new List<double>(x0.Count); |
|||
orderedBreakpoint.AddRange(breakpoint); |
|||
orderedBreakpoint.Sort(); |
|||
|
|||
// Compute initial state variables
|
|||
var d = -gradient; |
|||
for (int ii = 0; ii < d.Count; ++ii) |
|||
if (breakpoint[ii] <= 0.0) |
|||
d[ii] *= 0.0; |
|||
|
|||
|
|||
int jj = -1; |
|||
var x = x0; |
|||
var f1 = gradient * d; |
|||
var f2 = 0.5 * d * hessian * d; |
|||
var sMin = -f1 / f2; |
|||
var maxS = orderedBreakpoint[0]; |
|||
|
|||
if (sMin < maxS) |
|||
return new GradientProjectionResult(x + sMin * d, 0,isFixed); |
|||
|
|||
// while minimum of the last quadratic piece observed is beyond the interval searched
|
|||
while (true) |
|||
{ |
|||
// update data to the beginning of the interval we're searching
|
|||
jj += 1; |
|||
x = x + d * maxS; |
|||
maxS = orderedBreakpoint[jj+1] - orderedBreakpoint[jj]; |
|||
|
|||
int fixedCount = 0; |
|||
for (int ii = 0; ii < d.Count; ++ii) |
|||
if (orderedBreakpoint[jj] >= breakpoint[ii]) |
|||
{ |
|||
d[ii] *= 0.0; |
|||
isFixed[ii] = true; |
|||
fixedCount += 1; |
|||
} |
|||
|
|||
if (Double.IsPositiveInfinity(orderedBreakpoint[jj + 1])) |
|||
return new GradientProjectionResult(x, fixedCount, isFixed); |
|||
|
|||
f1 = gradient * d + (x - x0) * hessian * d; |
|||
f2 = d * hessian * d; |
|||
|
|||
sMin = -f1 / f2; |
|||
|
|||
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); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public struct GradientProjectionResult |
|||
{ |
|||
public GradientProjectionResult(Vector<double> cauchyPoint, int fixedCount, List<bool> isFixed) |
|||
{ |
|||
CauchyPoint = cauchyPoint; |
|||
FixedCount = fixedCount; |
|||
IsFixed = isFixed; |
|||
} |
|||
public Vector<double> CauchyPoint { get; } |
|||
public int FixedCount { get; } |
|||
public List<bool> IsFixed { get; } |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,253 @@ |
|||
// <copyright file="BfgsTest.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-2016 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 MathNet.Numerics.LinearAlgebra.Double; |
|||
using MathNet.Numerics.Optimization; |
|||
using NUnit.Framework; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Collections.Generic; |
|||
using MathNet.Numerics.UnitTests.OptimizationTests.TestFunctions; |
|||
using System.Collections; |
|||
using MathNet.Numerics.Optimization.ObjectiveFunctions; |
|||
using NUnit.Framework.Interfaces; |
|||
|
|||
namespace MathNet.Numerics.UnitTests.OptimizationTests |
|||
{ |
|||
[TestFixture] |
|||
public class BfgsBMinimizerTests |
|||
{ |
|||
[Test] |
|||
public void FindMinimum_Rosenbrock_Easy() |
|||
{ |
|||
var obj = ObjectiveFunction.Gradient(RosenbrockFunction.Value, RosenbrockFunction.Gradient); |
|||
var solver = new BfgsBMinimizer (1e-5, 1e-5, 1e-5, maximumIterations: 1000); |
|||
var lowerBound = new DenseVector(new[]{ -5.0, -5.0 }); |
|||
var upperBound = new DenseVector(new[]{ 5.0, 5.0 }); |
|||
var initialGuess = new DenseVector(new[] { 1.2, 1.2 }); |
|||
|
|||
var result = solver.FindMinimum(obj, lowerBound, upperBound, initialGuess); |
|||
|
|||
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 BfgsBMinimizer (1e-5, 1e-5, 1e-5, maximumIterations: 1000); |
|||
|
|||
var lowerBound = new DenseVector(new[]{ -5.0, -5.0 }); |
|||
var upperBound = new DenseVector(new[]{ 5.0, 5.0 }); |
|||
|
|||
var initialGuess = new DenseVector (new[]{ -1.2, 1.0 }); |
|||
|
|||
var result = solver.FindMinimum(obj, lowerBound, upperBound, initialGuess); |
|||
|
|||
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 BfgsBMinimizer (1e-5, 1e-5, 1e-5, maximumIterations: 1000); |
|||
|
|||
var lowerBound = new DenseVector(new[]{ -5.0, -5.0 }); |
|||
var upperBound = new DenseVector(new[]{ 5.0, 5.0 }); |
|||
var initialGuess = new DenseVector (new[]{ -0.9, -0.5 }); |
|||
|
|||
var result = solver.FindMinimum (obj, lowerBound, upperBound, initialGuess); |
|||
|
|||
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_Easy_OneBoundary() |
|||
{ |
|||
var obj = ObjectiveFunction.Gradient(RosenbrockFunction.Value, RosenbrockFunction.Gradient); |
|||
var solver = new BfgsBMinimizer (1e-5, 1e-5, 1e-5, maximumIterations: 1000); |
|||
var lowerBound = new DenseVector(new[]{ 1.0, -5.0 }); |
|||
var upperBound = new DenseVector(new[]{ 5.0, 5.0 }); |
|||
var initialGuess = new DenseVector(new[] { 1.2, 1.2 }); |
|||
|
|||
var result = solver.FindMinimum(obj, lowerBound, upperBound, initialGuess); |
|||
|
|||
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_Easy_TwoBoundaries() |
|||
{ |
|||
var obj = ObjectiveFunction.Gradient(RosenbrockFunction.Value, RosenbrockFunction.Gradient); |
|||
var solver = new BfgsBMinimizer (1e-5, 1e-5, 1e-5, maximumIterations: 1000); |
|||
var lowerBound = new DenseVector(new[]{ 1.0, 1.0 }); |
|||
var upperBound = new DenseVector(new[]{ 5.0, 5.0 }); |
|||
var initialGuess = new DenseVector(new[] { 1.2, 1.2 }); |
|||
|
|||
var result = solver.FindMinimum(obj, lowerBound, upperBound, initialGuess); |
|||
|
|||
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_MinimumGreateerOrEqualToLowerBoundary() |
|||
{ |
|||
var obj = ObjectiveFunction.Gradient(RosenbrockFunction.Value, RosenbrockFunction.Gradient); |
|||
var solver = new BfgsBMinimizer(1e-5, 1e-5, 1e-5, maximumIterations: 1000); |
|||
|
|||
var lowerBound = new DenseVector(new[] { 2, 2.0 }); |
|||
var upperBound = new DenseVector(new[] { 5.0, 5.0 }); |
|||
|
|||
var initialGuess = new DenseVector(new[] { 2.5, 2.5 }); |
|||
|
|||
var result = solver.FindMinimum(obj, lowerBound, upperBound, initialGuess); |
|||
|
|||
Assert.GreaterOrEqual(result.MinimizingPoint[0],lowerBound[0]); |
|||
Assert.GreaterOrEqual(result.MinimizingPoint[1], lowerBound[1]); |
|||
} |
|||
|
|||
[Test] |
|||
public void FindMinimum_Rosenbrock_MinimumLesserOrEqualToUpperBoundary() |
|||
{ |
|||
var obj = ObjectiveFunction.Gradient(RosenbrockFunction.Value, RosenbrockFunction.Gradient); |
|||
var solver = new BfgsBMinimizer(1e-5, 1e-5, 1e-5, maximumIterations: 1000); |
|||
|
|||
var lowerBound = new DenseVector(new[] { -2.0, -2.0 }); |
|||
var upperBound = new DenseVector(new[] { 0.5, 0.5 }); |
|||
|
|||
var initialGuess = new DenseVector(new[] { -0.9, -0.5 }); |
|||
|
|||
var result = solver.FindMinimum(obj, lowerBound, upperBound, initialGuess); |
|||
|
|||
Assert.LessOrEqual(result.MinimizingPoint[0],upperBound[0]); |
|||
Assert.LessOrEqual(result.MinimizingPoint[1],upperBound[1]); |
|||
} |
|||
|
|||
[Test] |
|||
[TestCaseSource(typeof(MghTestCaseEnumerator))] |
|||
public void Mgh_Tests(TestFunctions.TestCase test_case) |
|||
{ |
|||
var obj = new MghObjectiveFunction(test_case.Function, true, true); |
|||
var solver = new BfgsBMinimizer(1e-8, 1e-8, 1e-8, 1000); |
|||
|
|||
var result = solver.FindMinimum(obj, test_case.LowerBound, test_case.UpperBound, 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."); |
|||
} |
|||
|
|||
[Test] |
|||
[TestCaseSource(typeof(FdMghTestCaseEnumerator))] |
|||
public void Mgh_FiniteDifference_Tests(TestFunctions.TestCase test_case) |
|||
{ |
|||
var obj1 = new MghObjectiveFunction(test_case.Function, true, true); |
|||
var obj = new ForwardDifferenceGradientObjectiveFunction(obj1, test_case.LowerBound, test_case.UpperBound, 1e-10, 1e-10); |
|||
var solver = new BfgsBMinimizer(1e-8, 1e-8, 1e-8, 1000); |
|||
|
|||
var result = solver.FindMinimum(obj, test_case.LowerBound, test_case.UpperBound, 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."); |
|||
} |
|||
|
|||
|
|||
private class BaseMghTestCaseEnumerator : IEnumerable<ITestCaseData> |
|||
{ |
|||
private string _prefix = ""; |
|||
|
|||
public BaseMghTestCaseEnumerator(string prefix) |
|||
{ |
|||
if (prefix.EndsWith(" ")) |
|||
_prefix = prefix; |
|||
else |
|||
_prefix = prefix + " "; |
|||
} |
|||
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.IsBounded) |
|||
.Select(x => new TestCaseData(x) |
|||
.SetName(_prefix + x.FullName) |
|||
) |
|||
.GetEnumerator(); |
|||
} |
|||
|
|||
IEnumerator IEnumerable.GetEnumerator() |
|||
{ |
|||
return this.GetEnumerator(); |
|||
} |
|||
} |
|||
|
|||
private class MghTestCaseEnumerator : BaseMghTestCaseEnumerator |
|||
{ |
|||
public MghTestCaseEnumerator() : base("") { } |
|||
} |
|||
|
|||
private class FdMghTestCaseEnumerator : BaseMghTestCaseEnumerator |
|||
{ |
|||
public FdMghTestCaseEnumerator() : base("FD") { } |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,131 @@ |
|||
using System; |
|||
using System.Linq; |
|||
using MathNet.Numerics.LinearAlgebra.Double; |
|||
using MathNet.Numerics.Optimization; |
|||
using NUnit.Framework; |
|||
using System.Text; |
|||
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 BfgsMinimizerTests |
|||
{ |
|||
[Test] |
|||
public void FindMinimum_Rosenbrock_Easy() |
|||
{ |
|||
var obj = ObjectiveFunction.Gradient(RosenbrockFunction.Value, RosenbrockFunction.Gradient); |
|||
var solver = new BfgsMinimizer(1e-5, 1e-5, 1e-5, 1000); |
|||
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 BfgsMinimizer(1e-5, 1e-5, 1e-5, 1000); |
|||
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 BfgsMinimizer(1e-5, 1e-5, 1e-5, 1000); |
|||
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 BfgsMinimizer(1e-10, 1e-5, 1e-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 BfgsMinimizer(1e-5, 1e-5, 1e-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 BfgsMinimizer(1e-5, 1e-5, 1e-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(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 BfgsMinimizer(1e-8, 1e-8, 1e-8, 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."); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,56 @@ |
|||
// <copyright file="BfgsTest.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-2016 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.Double; |
|||
using MathNet.Numerics.Optimization; |
|||
using NUnit.Framework; |
|||
|
|||
namespace MathNet.Numerics.UnitTests.OptimizationTests |
|||
{ |
|||
[TestFixture, Category("RootFinding")] |
|||
internal class BfgsTest |
|||
{ |
|||
private const double Precision = 1e-4; |
|||
|
|||
[Test] |
|||
public void MinimizeRosenbrock() |
|||
{ |
|||
CheckRosenbrock(15.0, 8.0, expectedMin: 0.0); |
|||
CheckRosenbrock(-1.2, 1.0, expectedMin: 0.0); |
|||
CheckRosenbrock(-1.2, 100.0, expectedMin: 0.0); |
|||
} |
|||
|
|||
private static void CheckRosenbrock(double a, double b, double expectedMin) |
|||
{ |
|||
var x = BfgsSolver.Solve(new DenseVector(new[] { a, b }), RosenbrockFunction.Value, RosenbrockFunction.Gradient); |
|||
Numerics.Precision.AlmostEqual(expectedMin, RosenbrockFunction.Value(x), Precision); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,101 @@ |
|||
using System; |
|||
using MathNet.Numerics.LinearAlgebra.Double; |
|||
using MathNet.Numerics.Optimization; |
|||
using NUnit.Framework; |
|||
using MathNet.Numerics.UnitTests.OptimizationTests.TestFunctions; |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using NUnit.Framework.Interfaces; |
|||
|
|||
namespace MathNet.Numerics.UnitTests.OptimizationTests |
|||
{ |
|||
[TestFixture] |
|||
public class ConjugateGradientMinimizerTests |
|||
{ |
|||
[Test] |
|||
public void FindMinimum_Rosenbrock_Easy() |
|||
{ |
|||
var obj = ObjectiveFunction.Gradient(RosenbrockFunction.Value, RosenbrockFunction.Gradient); |
|||
var solver = new ConjugateGradientMinimizer(1e-5, 1000); |
|||
var result = solver.FindMinimum(obj, new DenseVector(new[]{1.2,1.2})); |
|||
|
|||
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 FindMinimum_Rosenbrock_Hard() |
|||
{ |
|||
var obj = ObjectiveFunction.Gradient(RosenbrockFunction.Value, RosenbrockFunction.Gradient); |
|||
var solver = new ConjugateGradientMinimizer(1e-5, 1000); |
|||
var result = solver.FindMinimum(obj, new DenseVector(new[] { -1.2, 1.0 })); |
|||
|
|||
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)); |
|||
} |
|||
|
|||
private class MghTestCaseEnumerator : IEnumerable<ITestCaseData> |
|||
{ |
|||
private static readonly string[] _ignore_list = |
|||
{ |
|||
"Beale fun (MGH #5) unbounded", |
|||
"Meyer fun (MGH #10) unbounded", |
|||
"Powell singular fun (MGH #13) unbounded", |
|||
"Rosenbrock fun (MGH #1) hard start", |
|||
"Rosenbrock fun (MGH #1) Overton start", |
|||
}; |
|||
|
|||
private static bool in_ignore_list(string test_name) |
|||
{ |
|||
return _ignore_list.Contains(test_name); |
|||
} |
|||
|
|||
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(x => new TestCaseData(x) |
|||
.SetName(x.FullName) |
|||
.IgnoreIf(in_ignore_list(x.FullName),"Algo error, not implementation error.") |
|||
) |
|||
.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 ConjugateGradientMinimizer(1e-8, 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."); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
using System; |
|||
using MathNet.Numerics.Optimization; |
|||
using NUnit.Framework; |
|||
|
|||
namespace MathNet.Numerics.UnitTests.OptimizationTests |
|||
{ |
|||
[TestFixture] |
|||
public class GoldenSectionMinimizerTests |
|||
{ |
|||
[Test] |
|||
public void Test_Works() |
|||
{ |
|||
var algorithm = new GoldenSectionMinimizer(1e-5, 1000); |
|||
var f1 = new Func<double, double>(x => (x - 3)*(x - 3)); |
|||
var obj = new SimpleObjectiveFunction1D(f1); |
|||
var r1 = algorithm.FindMinimum(obj, -100, 100); |
|||
|
|||
Assert.That(Math.Abs(r1.MinimizingPoint - 3.0), Is.LessThan(1e-4)); |
|||
} |
|||
|
|||
[Test] |
|||
public void Test_ExpansionWorks() |
|||
{ |
|||
var algorithm = new GoldenSectionMinimizer(1e-5, 1000); |
|||
var f1 = new Func<double, double>(x => (x - 3)*(x - 3)); |
|||
var obj = new SimpleObjectiveFunction1D(f1); |
|||
var r1 = algorithm.FindMinimum(obj, -5, 5); |
|||
|
|||
Assert.That(Math.Abs(r1.MinimizingPoint - 3.0), Is.LessThan(1e-4)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,133 @@ |
|||
// <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-2016 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.Double; |
|||
using MathNet.Numerics.Optimization; |
|||
using MathNet.Numerics.UnitTests.OptimizationTests.TestFunctions; |
|||
using NUnit.Framework; |
|||
using System; |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using NUnit.Framework.Interfaces; |
|||
|
|||
namespace MathNet.Numerics.UnitTests.OptimizationTests |
|||
{ |
|||
[TestFixture] |
|||
public class NelderMeadSimplexTests |
|||
{ |
|||
[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)); |
|||
} |
|||
|
|||
private class MghTestCaseEnumerator : IEnumerable<ITestCaseData> |
|||
{ |
|||
private static readonly string[] _ignore_list = |
|||
{ |
|||
"Meyer fun (MGH #10) unbounded", |
|||
}; |
|||
|
|||
private static bool in_ignore_list(string test_name) |
|||
{ |
|||
return _ignore_list.Contains(test_name); |
|||
} |
|||
|
|||
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(x => new TestCaseData(x) |
|||
.SetName(x.FullName) |
|||
.IgnoreIf(in_ignore_list(x.FullName), "Algo error, not implementation error") |
|||
) |
|||
.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 NelderMeadSimplex(1e-8, 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."); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,188 @@ |
|||
using System; |
|||
using MathNet.Numerics.LinearAlgebra.Double; |
|||
using MathNet.Numerics.Optimization; |
|||
using MathNet.Numerics.Optimization.ObjectiveFunctions; |
|||
using NUnit.Framework; |
|||
using MathNet.Numerics.UnitTests.OptimizationTests.TestFunctions; |
|||
using System.Collections.Generic; |
|||
using System.Collections; |
|||
using System.Linq; |
|||
using NUnit.Framework.Interfaces; |
|||
|
|||
namespace MathNet.Numerics.UnitTests.OptimizationTests |
|||
{ |
|||
public class LazyRosenbrockObjectiveFunction : LazyObjectiveFunctionBase |
|||
{ |
|||
public LazyRosenbrockObjectiveFunction() : base(true, true) { } |
|||
|
|||
public override IObjectiveFunction CreateNew() |
|||
{ |
|||
return new LazyRosenbrockObjectiveFunction(); |
|||
} |
|||
|
|||
protected override void EvaluateValue() |
|||
{ |
|||
Value = RosenbrockFunction.Value(Point); |
|||
} |
|||
|
|||
protected override void EvaluateGradient() |
|||
{ |
|||
Gradient = RosenbrockFunction.Gradient(Point); |
|||
} |
|||
|
|||
protected override void EvaluateHessian() |
|||
{ |
|||
Hessian = RosenbrockFunction.Hessian(Point); |
|||
} |
|||
} |
|||
|
|||
public class RosenbrockObjectiveFunction : ObjectiveFunctionBase |
|||
{ |
|||
public RosenbrockObjectiveFunction() : base(true, true) { } |
|||
|
|||
public override IObjectiveFunction CreateNew() |
|||
{ |
|||
return new RosenbrockObjectiveFunction(); |
|||
} |
|||
|
|||
protected override void Evaluate() |
|||
{ |
|||
// here we could directly overwrite the existing matrix cells instead.
|
|||
// note: values must then be initialized manually first, if null.
|
|||
Value = RosenbrockFunction.Value(Point); |
|||
Gradient = RosenbrockFunction.Gradient(Point); |
|||
Hessian = RosenbrockFunction.Hessian(Point); |
|||
} |
|||
} |
|||
|
|||
[TestFixture] |
|||
public class NewtonMinimizerTests |
|||
{ |
|||
[Test] |
|||
public void FindMinimum_Rosenbrock_Easy() |
|||
{ |
|||
var obj = ObjectiveFunction.GradientHessian(RosenbrockFunction.Value, RosenbrockFunction.Gradient, RosenbrockFunction.Hessian); |
|||
var solver = new NewtonMinimizer(1e-5, 1000); |
|||
var result = solver.FindMinimum(obj, new DenseVector(new[] { 1.2, 1.2 })); |
|||
|
|||
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 FindMinimum_Rosenbrock_Hard() |
|||
{ |
|||
var obj = ObjectiveFunction.GradientHessian(point => Tuple.Create(RosenbrockFunction.Value(point), RosenbrockFunction.Gradient(point), RosenbrockFunction.Hessian(point))); |
|||
var solver = new NewtonMinimizer(1e-5, 1000); |
|||
var result = solver.FindMinimum(obj, new DenseVector(new[] { -1.2, 1.0 })); |
|||
|
|||
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 FindMinimum_Rosenbrock_Overton() |
|||
{ |
|||
var obj = new LazyRosenbrockObjectiveFunction(); |
|||
var solver = new NewtonMinimizer(1e-5, 1000); |
|||
var result = solver.FindMinimum(obj, new DenseVector(new[] { -0.9, -0.5 })); |
|||
|
|||
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 FindMinimum_Linesearch_Rosenbrock_Easy() |
|||
{ |
|||
var obj = new RosenbrockObjectiveFunction(); |
|||
var solver = new NewtonMinimizer(1e-5, 1000, true); |
|||
var result = solver.FindMinimum(obj, new DenseVector(new[] { 1.2, 1.2 })); |
|||
|
|||
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 FindMinimum_Linesearch_Rosenbrock_Hard() |
|||
{ |
|||
var obj = new LazyRosenbrockObjectiveFunction(); |
|||
var solver = new NewtonMinimizer(1e-5, 1000, true); |
|||
var result = solver.FindMinimum(obj, new DenseVector(new[] { -1.2, 1.0 })); |
|||
|
|||
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 FindMinimum_Linesearch_Rosenbrock_Overton() |
|||
{ |
|||
var obj = new LazyRosenbrockObjectiveFunction(); |
|||
var solver = new NewtonMinimizer(1e-5, 1000, true); |
|||
var result = solver.FindMinimum(obj, new DenseVector(new[] { -0.9, -0.5 })); |
|||
|
|||
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)); |
|||
} |
|||
|
|||
private class MghTestCaseEnumerator : IEnumerable<ITestCaseData> |
|||
{ |
|||
private static readonly string[] _ignore_list = |
|||
{ |
|||
"Beale fun (MGH #5) unbounded", |
|||
"Meyer fun (MGH #10) unbounded", |
|||
"Wood fun (MGH #14) unbounded", |
|||
}; |
|||
|
|||
private static bool in_ignore_list(string test_name) |
|||
{ |
|||
return _ignore_list.Contains(test_name); |
|||
} |
|||
|
|||
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(x => new TestCaseData(x) |
|||
.SetName(x.FullName) |
|||
.IgnoreIf(in_ignore_list(x.FullName),"Algo error, not implementation error") |
|||
) |
|||
.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 NewtonMinimizer(1e-8, 1000, useLineSearch: false); |
|||
|
|||
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."); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,66 @@ |
|||
using System; |
|||
using MathNet.Numerics.LinearAlgebra; |
|||
using MathNet.Numerics.LinearAlgebra.Double; |
|||
|
|||
namespace MathNet.Numerics.UnitTests.OptimizationTests |
|||
{ |
|||
public static class RosenbrockFunction |
|||
{ |
|||
public static double Value(Vector<double> input) |
|||
{ |
|||
return Math.Pow((1 - input[0]), 2) + 100 * Math.Pow((input[1] - input[0] * input[0]), 2); |
|||
} |
|||
|
|||
public static Vector<double> Gradient(Vector<double> input) |
|||
{ |
|||
Vector<double> output = new DenseVector(2); |
|||
output[0] = -2 * (1 - input[0]) + 200 * (input[1] - input[0] * input[0]) * (-2 * input[0]); |
|||
output[1] = 2 * 100 * (input[1] - input[0] * input[0]); |
|||
return output; |
|||
} |
|||
|
|||
public static Matrix<double> Hessian(Vector<double> input) |
|||
{ |
|||
Matrix<double> output = new DenseMatrix(2, 2); |
|||
output[0, 0] = 2 - 400 * input[1] + 1200 * input[0] * input[0]; |
|||
output[1, 1] = 200; |
|||
output[0, 1] = -400 * input[0]; |
|||
output[1, 0] = output[0, 1]; |
|||
return output; |
|||
} |
|||
|
|||
public static Vector<double> Minimum |
|||
{ |
|||
get |
|||
{ |
|||
return new DenseVector(new double[] { 1, 1 }); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public static class BigRosenbrockFunction |
|||
{ |
|||
public static double Value(Vector<double> input) |
|||
{ |
|||
return 1000.0 + 100.0 * RosenbrockFunction.Value(input / 100.0); |
|||
} |
|||
|
|||
public static Vector<double> Gradient(Vector<double> input) |
|||
{ |
|||
return 100.0 * RosenbrockFunction.Gradient(input / 100.0); |
|||
} |
|||
|
|||
public static Matrix<double> Hessian(Vector<double> input) |
|||
{ |
|||
return 100.0 * RosenbrockFunction.Hessian(input / 100.0); |
|||
} |
|||
|
|||
public static Vector<double> Minimum |
|||
{ |
|||
get |
|||
{ |
|||
return new DenseVector(new double[] { 100, 100 }); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,55 @@ |
|||
using System; |
|||
using MathNet.Numerics.LinearAlgebra.Double; |
|||
using NUnit.Framework; |
|||
|
|||
namespace MathNet.Numerics.UnitTests.OptimizationTests |
|||
{ |
|||
[TestFixture] |
|||
class RosenbrockFunctionTests |
|||
{ |
|||
[Test] |
|||
public void TestGradient() |
|||
{ |
|||
var input = new DenseVector(new[]{ -0.9, -0.5 } ); |
|||
|
|||
var v1 = RosenbrockFunction.Value(input); |
|||
var g = RosenbrockFunction.Gradient(input); |
|||
|
|||
var eps = 1e-5; |
|||
var eps0 = (new DenseVector(new[] { 1.0, 0.0 })) * eps; |
|||
var eps1 = (new DenseVector(new[] { 0.0, 1.0 })) * eps; |
|||
|
|||
var g0 = (RosenbrockFunction.Value(input + eps0) - RosenbrockFunction.Value(input - eps0)) / (2 * eps); |
|||
var g1 = (RosenbrockFunction.Value(input + eps1) - RosenbrockFunction.Value(input - eps1)) / (2 * eps); |
|||
|
|||
Assert.That(Math.Abs(g0 - g[0]) < 1e-3); |
|||
Assert.That(Math.Abs(g1 - g[1]) < 1e-3); |
|||
} |
|||
|
|||
[Test] |
|||
public void TestHessian() |
|||
{ |
|||
var input = new DenseVector(new[] { -0.9, -0.5 }); |
|||
|
|||
var v1 = RosenbrockFunction.Value(input); |
|||
var h = RosenbrockFunction.Hessian(input); |
|||
|
|||
var eps = 1e-5; |
|||
|
|||
var eps0 = (new DenseVector(new[] { 1.0, 0.0 })) * eps; |
|||
var eps1 = (new DenseVector(new[] { 0.0, 1.0 })) * eps; |
|||
|
|||
var epsuu = (new DenseVector(new[] { 1.0, 1.0 })) * eps; |
|||
var epsud = (new DenseVector(new[] { 1.0, -1.0 })) * eps; |
|||
|
|||
var h00 = (RosenbrockFunction.Value(input + eps0) - 2*RosenbrockFunction.Value(input) + RosenbrockFunction.Value(input - eps0)) / (eps*eps); |
|||
var h11 = (RosenbrockFunction.Value(input + eps1) - 2 * RosenbrockFunction.Value(input) + RosenbrockFunction.Value(input - eps1)) / (eps * eps); |
|||
var h01 = (RosenbrockFunction.Value(input + epsuu) - RosenbrockFunction.Value(input + epsud) - RosenbrockFunction.Value(input - epsud) + RosenbrockFunction.Value(input - epsuu)) / (4*eps * eps); |
|||
|
|||
Assert.That(Math.Abs(h00 - h[0,0]) < 1e-3); |
|||
Assert.That(Math.Abs(h11 - h[1,1]) < 1e-3); |
|||
Assert.That(Math.Abs(h01 - h[0, 1]) < 1e-3); |
|||
Assert.That(Math.Abs(h01 - h[1, 0]) < 1e-3); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
using NUnit.Framework; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace MathNet.Numerics.UnitTests.OptimizationTests |
|||
{ |
|||
internal static class TestCaseDataExtensions |
|||
{ |
|||
public static TestCaseData IgnoreIf(this TestCaseData input, bool do_ignore, string reason) |
|||
{ |
|||
if (do_ignore) |
|||
return input.Ignore(reason); |
|||
else |
|||
return input; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,54 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using MathNet.Numerics.LinearAlgebra; |
|||
using MathNet.Numerics.Optimization; |
|||
using MathNet.Numerics.Optimization.ObjectiveFunctions; |
|||
using MathNet.Numerics.UnitTests.OptimizationTests.TestFunctions; |
|||
using MathNet.Numerics.LinearAlgebra.Double; |
|||
|
|||
namespace MathNet.Numerics.UnitTests.OptimizationTests |
|||
{ |
|||
public class MghObjectiveFunction : LazyObjectiveFunctionBase |
|||
{ |
|||
private ITestFunction TestFunction; |
|||
|
|||
public MghObjectiveFunction(ITestFunction testFunction, bool use_gradient, bool use_hessian) |
|||
: base(use_gradient, use_hessian) |
|||
{ |
|||
this.TestFunction = testFunction; |
|||
} |
|||
|
|||
public override IObjectiveFunction CreateNew() |
|||
{ |
|||
return new MghObjectiveFunction(this.TestFunction, this.IsGradientSupported, this.IsHessianSupported); |
|||
} |
|||
|
|||
protected override void EvaluateValue() |
|||
{ |
|||
this.Value = this.TestFunction.SsqValue(this.Point); |
|||
} |
|||
|
|||
protected override void EvaluateGradient() |
|||
{ |
|||
if (this.IsGradientSupported) |
|||
{ |
|||
if (this.GradientValue == null) |
|||
this.Gradient = new DenseVector(this.TestFunction.ParameterDimension); |
|||
this.TestFunction.SsqGradientByRef(this.Point, GradientValue); |
|||
} |
|||
} |
|||
|
|||
protected override void EvaluateHessian() |
|||
{ |
|||
if (this.IsHessianSupported) |
|||
{ |
|||
if (this.HessianValue == null) |
|||
this.Hessian = new DenseMatrix(this.TestFunction.ParameterDimension, this.TestFunction.ParameterDimension); |
|||
this.TestFunction.SsqHessianByRef(this.Point, HessianValue); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,309 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using MathNet.Numerics.UnitTests.OptimizationTests.TestFunctions; |
|||
using NUnit.Framework; |
|||
using MathNet.Numerics.LinearAlgebra; |
|||
using System.Collections; |
|||
|
|||
namespace MathNet.Numerics.UnitTests.OptimizationTests |
|||
{ |
|||
[TestFixture] |
|||
public class TestFunctionTests |
|||
{ |
|||
private static IEnumerable<TestFunctions.TestCase> MghCases |
|||
{ |
|||
get |
|||
{ |
|||
return Enumerable.Empty<TestFunctions.TestCase>() |
|||
.Concat(RosenbrockFunction2.TestCases) |
|||
.Concat(BealeFunction.TestCases) |
|||
.Concat(HelicalValleyFunction.TestCases) |
|||
.Concat(MeyerFunction.TestCases) |
|||
.Concat(PowellSingularFunction.TestCases) |
|||
.Concat(WoodFunction.TestCases) |
|||
.Concat(BrownAndDennisFunction.TestCases); |
|||
} |
|||
} |
|||
|
|||
private class MghCaseEnumerator : IEnumerable<TestCaseData> |
|||
{ |
|||
public string CategoryName { get; protected set; } |
|||
|
|||
public MghCaseEnumerator(string category_name) |
|||
{ |
|||
this.CategoryName = category_name; |
|||
} |
|||
|
|||
public virtual IEnumerator<TestCaseData> GetEnumerator() |
|||
{ |
|||
return MghCases |
|||
.Select(x => |
|||
new TestCaseData(x) |
|||
.SetName($"{x.FullName} {this.CategoryName}") |
|||
).GetEnumerator(); |
|||
} |
|||
|
|||
IEnumerator IEnumerable.GetEnumerator() |
|||
{ |
|||
return this.GetEnumerator(); |
|||
} |
|||
} |
|||
|
|||
[Test] |
|||
public void Smoke_Construction() |
|||
{ |
|||
var c = new TestCase() |
|||
{ |
|||
InitialGuess = new double[] { 1, 2, 3 }, |
|||
MinimizingPoint = new double[] { 1, 1, 1 }, |
|||
MinimalValue = 0 |
|||
}; |
|||
} |
|||
|
|||
private class ValueAtMinimumSource : MghCaseEnumerator |
|||
{ |
|||
public ValueAtMinimumSource() : base("ValueAtMinimum") { } |
|||
|
|||
public override IEnumerator<TestCaseData> GetEnumerator() |
|||
{ |
|||
return MghCases |
|||
.Where(x => x.MinimizingPoint != null) |
|||
.Select(x => |
|||
new TestCaseData(x) |
|||
.SetName($"{x.FullName} {this.CategoryName}") |
|||
) |
|||
.GetEnumerator(); |
|||
} |
|||
} |
|||
|
|||
[Test] |
|||
[TestCaseSource(typeof(ValueAtMinimumSource))] |
|||
public void ValueAtMinimum(TestFunctions.TestCase test_case) |
|||
{ |
|||
if (test_case.MinimizingPoint != null) |
|||
{ |
|||
var value_at_minimum = test_case.Function.SsqValue(test_case.MinimizingPoint); |
|||
Assert.That( |
|||
Math.Abs(value_at_minimum - test_case.MinimalValue) < 1e-3, |
|||
$"Function value at minimum not as expected." |
|||
); |
|||
} |
|||
} |
|||
|
|||
private class GradientAtStartSource : MghCaseEnumerator |
|||
{ |
|||
public GradientAtStartSource() : base("GradientAtStart") { } |
|||
} |
|||
|
|||
[Test] |
|||
[TestCaseSource(typeof(GradientAtStartSource))] |
|||
public void GradientAtStart(TestFunctions.TestCase test_case) |
|||
{ |
|||
var a_grad = test_case.Function.SsqGradient(test_case.InitialGuess); |
|||
var fd_grad = Vector<double>.Build.Dense(test_case.Function.ParameterDimension, 0.0); |
|||
|
|||
for (int ii = 0; ii < test_case.Function.ParameterDimension; ++ii) |
|||
{ |
|||
var h = 1e-6; |
|||
|
|||
var bump_up = test_case.InitialGuess.Clone(); |
|||
bump_up[ii] += h; |
|||
var bump_down = test_case.InitialGuess.Clone(); |
|||
bump_down[ii] -= h; |
|||
|
|||
var up_val = test_case.Function.SsqValue(bump_up); |
|||
var down_val = test_case.Function.SsqValue(bump_down); |
|||
|
|||
fd_grad[ii] = 0.5 * (up_val - down_val) / h; |
|||
} |
|||
|
|||
for (int ii = 0; ii < test_case.Function.ParameterDimension; ++ii) |
|||
{ |
|||
var val1 = a_grad[ii]; |
|||
var val2 = fd_grad[ii]; |
|||
var min_abs_val = Math.Min(Math.Abs(val1), Math.Abs(val2)); |
|||
if (min_abs_val <= 1) |
|||
Assert.That(Math.Abs(val1 - val2) < 1e-3, $"Problem with gradient value at start point."); |
|||
else |
|||
Assert.That(Math.Abs(val1 - val2) / min_abs_val < 1e-3, $"Problem with gradient value at start point."); |
|||
} |
|||
} |
|||
|
|||
private class HessianAtStartSource : MghCaseEnumerator |
|||
{ |
|||
public HessianAtStartSource() : base("HessianAtStart") { } |
|||
|
|||
public override IEnumerator<TestCaseData> GetEnumerator() |
|||
{ |
|||
return MghCases |
|||
.Where(x => x.MinimizingPoint != null) |
|||
.Select(x => |
|||
new TestCaseData(x) |
|||
.SetName($"{x.FullName} {this.CategoryName}") |
|||
) |
|||
.GetEnumerator(); |
|||
} |
|||
} |
|||
[Test] |
|||
[TestCaseSource(typeof(HessianAtStartSource))] |
|||
public void HessianAtStart(TestFunctions.TestCase test_case) |
|||
{ |
|||
var a_hess = test_case.Function.SsqHessian(test_case.InitialGuess); |
|||
var fd_hess = Matrix<double>.Build.Dense(test_case.Function.ParameterDimension, test_case.Function.ParameterDimension); |
|||
for (int ii = 0; ii < test_case.Function.ParameterDimension; ++ii) |
|||
{ |
|||
for (int jj = 0; jj < test_case.Function.ParameterDimension; ++jj) |
|||
{ |
|||
var h1 = 1e-3 * Math.Max(1.0, Math.Abs(test_case.InitialGuess[ii])); |
|||
var h2 = 1e-3 * Math.Max(1.0, Math.Abs(test_case.InitialGuess[jj])); |
|||
|
|||
var bump_uu = test_case.InitialGuess.Clone(); |
|||
bump_uu[ii] += h1; |
|||
bump_uu[jj] += h2; |
|||
|
|||
var bump_dd = test_case.InitialGuess.Clone(); |
|||
bump_dd[ii] -= h1; |
|||
bump_dd[jj] -= h2; |
|||
|
|||
var bump_ud = test_case.InitialGuess.Clone(); |
|||
bump_ud[ii] += h1; |
|||
bump_ud[jj] -= h2; |
|||
|
|||
var bump_du = test_case.InitialGuess.Clone(); |
|||
bump_du[ii] -= h1; |
|||
bump_du[jj] += h2; |
|||
|
|||
var val_uu = test_case.Function.SsqValue(bump_uu); |
|||
var val_dd = test_case.Function.SsqValue(bump_dd); |
|||
var val_ud = test_case.Function.SsqValue(bump_ud); |
|||
var val_du = test_case.Function.SsqValue(bump_du); |
|||
|
|||
fd_hess[ii, jj] = (val_uu - val_ud + val_dd - val_du) / (4 * h1 * h2); |
|||
} |
|||
} |
|||
|
|||
for (int ii = 0; ii < test_case.Function.ParameterDimension; ++ii) |
|||
{ |
|||
for (int jj = 0; jj < test_case.Function.ParameterDimension; ++jj) |
|||
{ |
|||
var val1 = fd_hess[ii, jj]; |
|||
var val2 = a_hess[ii, jj]; |
|||
|
|||
var abs_min = Math.Min(Math.Abs(val1), Math.Abs(val2)); |
|||
if (abs_min <= 1) |
|||
{ |
|||
Assert.That(Math.Abs(val1 - val2) < 1e-3, $"Problem with hessian at start point."); |
|||
} |
|||
else |
|||
{ |
|||
Assert.That(Math.Abs(val1 - val2) / abs_min < 0.05, $"Problem with hessian at start point."); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
private class ItemGradientAtStartSource : MghCaseEnumerator |
|||
{ |
|||
public ItemGradientAtStartSource() : base("ItemGradientAtStart") { } |
|||
} |
|||
|
|||
[Test] |
|||
[TestCaseSource(typeof(ItemGradientAtStartSource))] |
|||
public void ItemGradientAtStart(TestFunctions.TestCase test_case) |
|||
{ |
|||
for (var item_index = 0; item_index < test_case.Function.ItemDimension; ++item_index) |
|||
{ |
|||
|
|||
var a_grad = test_case.Function.ItemGradient(test_case.InitialGuess, item_index); |
|||
var h = 1e-4; |
|||
var fd_grad = Vector<double>.Build.Dense(test_case.Function.ParameterDimension, 0.0); |
|||
|
|||
for (int ii = 0; ii < test_case.Function.ParameterDimension; ++ii) |
|||
{ |
|||
var bump_up = test_case.InitialGuess.Clone(); |
|||
bump_up[ii] += h; |
|||
var bump_down = test_case.InitialGuess.Clone(); |
|||
bump_down[ii] -= h; |
|||
|
|||
var up_val = test_case.Function.ItemValue(bump_up, item_index); |
|||
var down_val = test_case.Function.ItemValue(bump_down, item_index); |
|||
|
|||
fd_grad[ii] = 0.5 * (up_val - down_val) / h; |
|||
} |
|||
|
|||
for (int ii = 0; ii < test_case.Function.ParameterDimension; ++ii) |
|||
{ |
|||
Assert.That(Math.Abs(fd_grad[ii] - a_grad[ii]) < 1e-3, $"Failed for parameter {ii}"); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private class ItemHessianAtStartSource : MghCaseEnumerator |
|||
{ |
|||
public ItemHessianAtStartSource() : base("ItemHessianAtStart") { } |
|||
} |
|||
|
|||
[Test] |
|||
[TestCaseSource(typeof(ItemHessianAtStartSource))] |
|||
public void ItemHessianAtStart(TestFunctions.TestCase test_case) |
|||
{ |
|||
for (var item_index = 0; item_index < test_case.Function.ItemDimension; ++item_index) |
|||
{ |
|||
var a_hess = test_case.Function.ItemHessian(test_case.InitialGuess, item_index); |
|||
var h = 1e-4; |
|||
var fd_hess = Matrix<double>.Build.Dense(test_case.Function.ParameterDimension, test_case.Function.ParameterDimension); |
|||
for (int ii = 0; ii < test_case.Function.ParameterDimension; ++ii) |
|||
{ |
|||
for (int jj = 0; jj < test_case.Function.ParameterDimension; ++jj) |
|||
{ |
|||
var bump_uu = test_case.InitialGuess.Clone(); |
|||
bump_uu[ii] += h; |
|||
bump_uu[jj] += h; |
|||
|
|||
var bump_dd = test_case.InitialGuess.Clone(); |
|||
bump_dd[ii] -= h; |
|||
bump_dd[jj] -= h; |
|||
|
|||
var bump_ud = test_case.InitialGuess.Clone(); |
|||
bump_ud[ii] += h; |
|||
bump_ud[jj] -= h; |
|||
|
|||
var bump_du = test_case.InitialGuess.Clone(); |
|||
bump_du[ii] -= h; |
|||
bump_du[jj] += h; |
|||
|
|||
var val_uu = test_case.Function.ItemValue(bump_uu, item_index); |
|||
var val_dd = test_case.Function.ItemValue(bump_dd, item_index); |
|||
var val_ud = test_case.Function.ItemValue(bump_ud, item_index); |
|||
var val_du = test_case.Function.ItemValue(bump_du, item_index); |
|||
|
|||
fd_hess[ii, jj] = (val_uu - val_ud + val_dd - val_du) / (4 * h * h); |
|||
} |
|||
} |
|||
|
|||
for (int ii = 0; ii < test_case.Function.ParameterDimension; ++ii) |
|||
{ |
|||
for (int jj = 0; jj < test_case.Function.ParameterDimension; ++jj) |
|||
{ |
|||
var val1 = fd_hess[ii, jj]; |
|||
var val2 = a_hess[ii, jj]; |
|||
|
|||
var abs_min = Math.Min(Math.Abs(val1), Math.Abs(val2)); |
|||
if (abs_min <= 1) |
|||
{ |
|||
Assert.That(Math.Abs(val1 - val2) < 1e-3, $"Problem with hessian at start point."); |
|||
} |
|||
else |
|||
{ |
|||
Assert.That(Math.Abs(val1 - val2) / abs_min < 0.05, $"Problem with hessian at start point."); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,125 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using MathNet.Numerics.LinearAlgebra; |
|||
|
|||
namespace MathNet.Numerics.UnitTests.OptimizationTests.TestFunctions |
|||
{ |
|||
public abstract class BaseTestFunction : ITestFunction |
|||
{ |
|||
public abstract string Description { get; } |
|||
public abstract int ParameterDimension { get; } |
|||
public abstract int ItemDimension { get; } |
|||
|
|||
public abstract double ItemValue(Vector<double> x, int itemIndex); |
|||
public abstract void ItemGradientByRef(Vector<double> x, int itemIndex, Vector<double> output); |
|||
public abstract void ItemHessianByRef(Vector<double> x, int itemIndex, Matrix<double> output); |
|||
|
|||
public virtual Vector<double> ItemGradient(Vector<double> x, int itemIndex) |
|||
{ |
|||
var output = new LinearAlgebra.Double.DenseVector(this.ParameterDimension); |
|||
this.ItemGradientByRef(x, itemIndex, output); |
|||
return output; |
|||
} |
|||
|
|||
public virtual Matrix<double> ItemHessian(Vector<double> x, int itemIndex) |
|||
{ |
|||
var output = new LinearAlgebra.Double.DenseMatrix(this.ParameterDimension, this.ParameterDimension); |
|||
this.ItemHessianByRef(x, itemIndex, output); |
|||
return output; |
|||
} |
|||
|
|||
|
|||
public virtual void JacobianbyRef(Vector<double> x, Matrix<double> output) |
|||
{ |
|||
for (int ii = 0; ii < this.ItemDimension; ++ii) |
|||
{ |
|||
var grad = this.ItemGradient(x, ii); |
|||
output.SetRow(ii, grad); |
|||
} |
|||
} |
|||
|
|||
public virtual Matrix<double> Jacobian(Vector<double> x) |
|||
{ |
|||
var output = new LinearAlgebra.Double.DenseMatrix(this.ItemDimension, this.ParameterDimension); |
|||
this.JacobianbyRef(x, output); |
|||
return output; |
|||
} |
|||
|
|||
public virtual void SsqGradientByRef(Vector<double> x, Vector<double> output) |
|||
{ |
|||
if (output.Count != this.ParameterDimension) |
|||
throw new ArgumentException($"Output vector must match parameter dimension of function; expected {this.ParameterDimension}, got {output.Count}."); |
|||
|
|||
for (int jj = 0; jj < this.ParameterDimension; ++jj) |
|||
output[jj] = 0.0; |
|||
var tmp_grad = new LinearAlgebra.Double.DenseVector(this.ParameterDimension); |
|||
double tmp_value = 0.0; |
|||
|
|||
for (int ii = 0; ii < this.ItemDimension; ++ii) |
|||
{ |
|||
tmp_value = this.ItemValue(x, ii); |
|||
this.ItemGradientByRef(x, ii, tmp_grad); |
|||
for (int jj = 0; jj < this.ParameterDimension; ++jj) |
|||
output[jj] += 2 * tmp_value * tmp_grad[jj]; |
|||
} |
|||
} |
|||
|
|||
public virtual Vector<double> SsqGradient(Vector<double> x) |
|||
{ |
|||
var output = new LinearAlgebra.Double.DenseVector(this.ParameterDimension); |
|||
this.SsqGradientByRef(x, output); |
|||
return output; |
|||
} |
|||
|
|||
public virtual void SsqHessianByRef(Vector<double> x, Matrix<double> output) |
|||
{ |
|||
if (output.RowCount != this.ParameterDimension || output.ColumnCount != this.ParameterDimension) |
|||
throw new ArgumentException($"Output matrix must match parameter dimension of function; expected {this.ParameterDimension}x{this.ParameterDimension}, got {output.RowCount}x{output.ColumnCount}."); |
|||
|
|||
for (int ii = 0; ii < this.ParameterDimension; ++ii) |
|||
for (int jj = 0; jj < this.ParameterDimension; ++jj) |
|||
output[ii,jj] = 0.0; |
|||
|
|||
var tmp_grad = new LinearAlgebra.Double.DenseVector(this.ParameterDimension); |
|||
var tmp_hess = new LinearAlgebra.Double.DenseMatrix(this.ParameterDimension, this.ParameterDimension); |
|||
double tmp_value = 0.0; |
|||
|
|||
for (int ii = 0; ii < this.ItemDimension; ++ii) |
|||
{ |
|||
tmp_value = this.ItemValue(x, ii); |
|||
this.ItemGradientByRef(x, ii, tmp_grad); |
|||
this.ItemHessianByRef(x, ii, tmp_hess); |
|||
for (int jj = 0; jj < this.ParameterDimension; ++jj) |
|||
{ |
|||
for (int kk = 0; kk < this.ParameterDimension; ++kk) |
|||
{ |
|||
var increment = 2 * (tmp_value * tmp_hess[jj, kk] + tmp_grad[jj] * tmp_grad[kk]); |
|||
output[jj, kk] += increment; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
public virtual Matrix<double> SsqHessian(Vector<double> x) |
|||
{ |
|||
var output = new LinearAlgebra.Double.DenseMatrix(this.ParameterDimension, this.ParameterDimension); |
|||
this.SsqHessianByRef(x, output); |
|||
return output; |
|||
} |
|||
|
|||
public virtual double SsqValue(Vector<double> x) |
|||
{ |
|||
double ssq = 0.0; |
|||
for (int ii = 0; ii < this.ItemDimension; ++ii) |
|||
{ |
|||
var tmp = this.ItemValue(x, ii); |
|||
ssq += tmp * tmp; |
|||
} |
|||
return ssq; |
|||
} |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,97 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using MathNet.Numerics.LinearAlgebra; |
|||
|
|||
namespace MathNet.Numerics.UnitTests.OptimizationTests.TestFunctions |
|||
{ |
|||
public class BealeFunction : BaseTestFunction |
|||
{ |
|||
public static IEnumerable<TestCase> TestCases |
|||
{ |
|||
get |
|||
{ |
|||
yield return new TestCase() |
|||
{ |
|||
Function = new BealeFunction(), |
|||
InitialGuess = new double[] { 1, 1 }, |
|||
MinimalValue = 0, |
|||
MinimizingPoint = new double[] { 3, 0.5 }, |
|||
CaseName = "unbounded" |
|||
}; |
|||
yield return new TestCase() |
|||
{ |
|||
Function = new BealeFunction(), |
|||
InitialGuess = new double[] { 1, 1 }, |
|||
MinimalValue = 0, |
|||
MinimizingPoint = new double[] { 3, 0.5 }, |
|||
LowerBound = new double[] { -1000, -1000}, |
|||
UpperBound = new double[] { 1000, 1000}, |
|||
CaseName = "loose bounds" |
|||
}; |
|||
yield return new TestCase() |
|||
{ |
|||
Function = new BealeFunction(), |
|||
InitialGuess = new double[] { 1, 1 }, |
|||
MinimalValue = 0, |
|||
MinimizingPoint = new double[] { 3, 0.5 }, |
|||
LowerBound = new double[] { 0.6, 0.5 }, |
|||
UpperBound = new double[] { 10, 100 }, |
|||
CaseName = "tight bounds" |
|||
}; |
|||
} |
|||
} |
|||
|
|||
public BealeFunction() { } |
|||
|
|||
public override string Description |
|||
{ |
|||
get |
|||
{ |
|||
return "Beale fun (MGH #5)"; |
|||
} |
|||
} |
|||
|
|||
public override int ItemDimension |
|||
{ |
|||
get |
|||
{ |
|||
return 3; |
|||
} |
|||
} |
|||
|
|||
public override int ParameterDimension |
|||
{ |
|||
get |
|||
{ |
|||
return 2; |
|||
} |
|||
} |
|||
|
|||
public override void ItemGradientByRef(Vector<double> x, int itemIndex, Vector<double> output) |
|||
{ |
|||
int ii = itemIndex + 1; |
|||
output[0] = -1 + Math.Pow(x[1], ii); |
|||
output[1] = ii * x[0] * Math.Pow(x[1], ii - 1); |
|||
} |
|||
|
|||
public override void ItemHessianByRef(Vector<double> x, int itemIndex, Matrix<double> output) |
|||
{ |
|||
int ii = itemIndex + 1; |
|||
output[0, 0] = 0; |
|||
output[0, 1] = ii * Math.Pow(x[1], ii - 1); |
|||
output[1, 0] = ii * Math.Pow(x[1], ii - 1); |
|||
output[1, 1] = (ii - 1) * ii * x[0] * Math.Pow(x[1], ii - 2); |
|||
} |
|||
|
|||
private static readonly double[] y = { 1.5, 2.25, 2.625}; |
|||
|
|||
public override double ItemValue(Vector<double> x, int itemIndex) |
|||
{ |
|||
int ii = itemIndex + 1; |
|||
return y[itemIndex] - x[0] * (1 - Math.Pow(x[1], ii)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,114 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using MathNet.Numerics.LinearAlgebra; |
|||
|
|||
namespace MathNet.Numerics.UnitTests.OptimizationTests.TestFunctions |
|||
{ |
|||
public class BrownAndDennisFunction : BaseTestFunction |
|||
{ |
|||
public static IEnumerable<TestCase> TestCases |
|||
{ |
|||
get |
|||
{ |
|||
yield return new TestCase() |
|||
{ |
|||
Function = new BrownAndDennisFunction(20), |
|||
InitialGuess = new double[] { 25, 5, -5, -1 }, |
|||
MinimalValue = 85822.2, |
|||
MinimizingPoint = null, |
|||
CaseName = "unbounded" |
|||
}; |
|||
yield return new TestCase() |
|||
{ |
|||
Function = new BrownAndDennisFunction(20), |
|||
InitialGuess = new double[] { 25, 5, -5, -1 }, |
|||
MinimalValue = 85822.2, |
|||
MinimizingPoint = null, |
|||
LowerBound = new double[] { -1000, -1000, -1000, -1000 }, |
|||
UpperBound = new double[] {1000, 1000, 1000, 1000 }, |
|||
CaseName = "loose bounds" |
|||
}; |
|||
yield return new TestCase() |
|||
{ |
|||
Function = new BrownAndDennisFunction(20), |
|||
InitialGuess = new double[] { 25, 5, -5, -1 }, |
|||
MinimalValue = 0.88860479e5, |
|||
MinimizingPoint = null, |
|||
LowerBound = new double[] { -10, 0, -100, -20 }, |
|||
UpperBound = new double[] { 100, 15, 0, 0.2 }, |
|||
CaseName = "tight bounds" |
|||
}; |
|||
} |
|||
} |
|||
|
|||
private readonly int _items; |
|||
|
|||
public BrownAndDennisFunction(int items) |
|||
{ |
|||
if (items < 4) |
|||
throw new ArgumentException("items must be >= 4"); |
|||
_items = items; |
|||
} |
|||
|
|||
public override string Description |
|||
{ |
|||
get |
|||
{ |
|||
return "Brown & Dennis fun (MGH #16)"; |
|||
} |
|||
} |
|||
|
|||
public override int ItemDimension |
|||
{ |
|||
get |
|||
{ |
|||
return _items; |
|||
} |
|||
} |
|||
|
|||
public override int ParameterDimension |
|||
{ |
|||
get |
|||
{ |
|||
return 4; |
|||
} |
|||
} |
|||
|
|||
public override void ItemGradientByRef(Vector<double> x, int itemIndex, Vector<double> output) |
|||
{ |
|||
var ii = itemIndex + 1; |
|||
var t = ii / 5.0; |
|||
output[0] = 2 * (x[0] + t * x[1] - Math.Exp(t)); |
|||
output[1] = (2*ii/25.0) * (5 * x[0] + ii * x[1] - 5 * Math.Exp(t)); |
|||
output[2] = 2 * (x[2] + x[3] * Math.Sin(t) - Math.Cos(t)); |
|||
output[3] = 2 * Math.Sin(t) * (x[2] + Math.Sin(t) * x[3] - Math.Cos(t)); |
|||
} |
|||
|
|||
public override void ItemHessianByRef(Vector<double> x, int itemIndex, Matrix<double> output) |
|||
{ |
|||
for (int ii = 0; ii < 4; ++ii) |
|||
for (int jj = 0; jj < 4; ++jj) |
|||
output[ii, jj] = 0; |
|||
var i = itemIndex + 1; |
|||
var t = i / 5.0; |
|||
output[0, 0] = 2; |
|||
output[0, 1] = 2 * t; |
|||
output[1, 0] = 2 * t; |
|||
output[1, 1] = 2 * t * t; |
|||
output[2, 2] = 2; |
|||
output[2, 3] = 2 * Math.Sin(t); |
|||
output[3, 2] = 2 * Math.Sin(t); |
|||
output[3, 3] = 2 * Math.Pow(Math.Sin(t), 2); |
|||
} |
|||
|
|||
public override double ItemValue(Vector<double> x, int itemIndex) |
|||
{ |
|||
var ii = itemIndex + 1; |
|||
var t = ii / 5.0; |
|||
return Math.Pow(x[0] + t * x[1] - Math.Exp(t), 2.0) + Math.Pow(x[2] + x[3] * Math.Sin(t) - Math.Cos(t), 2); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,131 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using MathNet.Numerics.LinearAlgebra; |
|||
|
|||
namespace MathNet.Numerics.UnitTests.OptimizationTests.TestFunctions |
|||
{ |
|||
public class BrownBadlyScaledFunction : BaseTestFunction |
|||
{ |
|||
public static IEnumerable<TestCase> TestCases |
|||
{ |
|||
get |
|||
{ |
|||
yield return new TestCase() |
|||
{ |
|||
Function = new BrownBadlyScaledFunction(), |
|||
InitialGuess = new double[] { 1, 1 }, |
|||
MinimalValue = 0, |
|||
MinimizingPoint = new double[] { 1e6, 2e-6 }, |
|||
CaseName = "unbounded" |
|||
}; |
|||
yield return new TestCase() |
|||
{ |
|||
Function = new BrownBadlyScaledFunction(), |
|||
InitialGuess = new double[] { 1, 1 }, |
|||
MinimalValue = 0, |
|||
MinimizingPoint = new double[] { 1e6, 2e-6 }, |
|||
LowerBound = new double[] { -1e8, -1e8 }, |
|||
UpperBound = new double[] { 1e8, 1e8 }, |
|||
CaseName = "loose bounds" |
|||
}; |
|||
yield return new TestCase() |
|||
{ |
|||
Function = new BrownBadlyScaledFunction(), |
|||
InitialGuess = new double[] { 1, 1 }, |
|||
MinimalValue = 0.784e3, |
|||
MinimizingPoint = new double[] { 1e6, 2e-6 }, |
|||
LowerBound = new double[] { 0, 3e-5 }, |
|||
UpperBound = new double[] { 1e6, 100 }, |
|||
CaseName = "tight bounds" |
|||
}; |
|||
} |
|||
} |
|||
|
|||
public BrownBadlyScaledFunction() { } |
|||
|
|||
public override string Description |
|||
{ |
|||
get |
|||
{ |
|||
return "Brown badly scaled fun (MGH #4)"; |
|||
} |
|||
} |
|||
|
|||
public override int ItemDimension |
|||
{ |
|||
get |
|||
{ |
|||
return 3; |
|||
} |
|||
} |
|||
|
|||
public override int ParameterDimension |
|||
{ |
|||
get |
|||
{ |
|||
return 2; |
|||
} |
|||
} |
|||
|
|||
public override void ItemGradientByRef(Vector<double> x, int itemIndex, Vector<double> output) |
|||
{ |
|||
switch (itemIndex) |
|||
{ |
|||
case 0: |
|||
output[0] = 1; |
|||
output[1] = 0; |
|||
break; |
|||
case 1: |
|||
output[0] = 0; |
|||
output[1] = 1; |
|||
break; |
|||
case 2: |
|||
output[0] = x[1]; |
|||
output[1] = x[0]; |
|||
break; |
|||
default: |
|||
throw new ArgumentException("itemIndex must be <= 2"); |
|||
} |
|||
} |
|||
|
|||
public override void ItemHessianByRef(Vector<double> x, int itemIndex, Matrix<double> output) |
|||
{ |
|||
switch (itemIndex) |
|||
{ |
|||
case 0: |
|||
case 1: |
|||
output[0, 0] = 0; |
|||
output[0, 1] = 0; |
|||
output[1, 0] = 0; |
|||
output[1, 1] = 0; |
|||
break; |
|||
case 2: |
|||
output[0, 0] = 0; |
|||
output[0, 1] = 1; |
|||
output[1, 0] = 1; |
|||
output[1, 1] = 0; |
|||
break; |
|||
default: |
|||
throw new ArgumentException("itemIndex must be <= 2"); |
|||
} |
|||
} |
|||
|
|||
public override double ItemValue(Vector<double> x, int itemIndex) |
|||
{ |
|||
switch (itemIndex) |
|||
{ |
|||
case 0: |
|||
return x[0] - 1e6; |
|||
case 1: |
|||
return x[1] - 2e-6; |
|||
case 2: |
|||
return x[0] * x[1] - 2; |
|||
default: |
|||
throw new ArgumentException("itemIndex must be <= 2"); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,98 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using MathNet.Numerics.LinearAlgebra; |
|||
using DenseVector = MathNet.Numerics.LinearAlgebra.Double.DenseVector; |
|||
using DenseMatrix = MathNet.Numerics.LinearAlgebra.Double.DenseMatrix; |
|||
|
|||
namespace MathNet.Numerics.UnitTests.OptimizationTests.TestFunctions |
|||
{ |
|||
public class FreudensteinAndRothFunction : BaseTestFunction |
|||
{ |
|||
|
|||
public static IEnumerable<TestCase> TestCases |
|||
{ |
|||
get |
|||
{ |
|||
yield return new TestCase() |
|||
{ |
|||
Function = new FreudensteinAndRothFunction(), |
|||
InitialGuess = new double[] { 0.5, -2 }, |
|||
MinimizingPoint = new double[] { 5, 4 }, |
|||
MinimalValue = 0, |
|||
CaseName = "unbounded" |
|||
}; |
|||
yield return new TestCase() |
|||
{ |
|||
Function = new FreudensteinAndRothFunction(), |
|||
InitialGuess = new double[] { 0.5, -2 }, |
|||
MinimizingPoint = new double[] {5, 4}, |
|||
MinimalValue = 0, |
|||
LowerBound = new double[] { -1000, -1000 }, |
|||
UpperBound = new double[] { 1000, 1000}, |
|||
CaseName = "loose bounds" |
|||
}; |
|||
} |
|||
} |
|||
|
|||
public override string Description { get { return "Freudenstein & Roth fun (MGH #2)"; } } |
|||
|
|||
public override int ParameterDimension |
|||
{ |
|||
get |
|||
{ |
|||
return 2; |
|||
} |
|||
} |
|||
|
|||
public override int ItemDimension |
|||
{ |
|||
get |
|||
{ |
|||
return 2; |
|||
} |
|||
} |
|||
|
|||
public override double ItemValue(Vector<double> x, int itemIndex) |
|||
{ |
|||
if (itemIndex == 0) |
|||
return -13 + x[0] + ((5 - x[1]) * x[1] - 2) * x[1]; |
|||
else |
|||
return -29 + x[0] + ((x[1] + 1) * x[1] - 14) * x[1]; |
|||
} |
|||
|
|||
public override void ItemGradientByRef(Vector<double> x, int itemIndex, Vector<double> output) |
|||
{ |
|||
if (itemIndex == 0) |
|||
{ |
|||
output[0] = 1; |
|||
output[1] = -2 + (5 - 2 * x[1]) * x[1] + (5 - x[1]) * x[1]; |
|||
} |
|||
else |
|||
{ |
|||
output[0] = 1; |
|||
output[1] = -14 + x[1] * (1 + x[1]) + x[1] * (1 + 2 * x[1]); |
|||
} |
|||
} |
|||
|
|||
public override void ItemHessianByRef(Vector<double> x, int itemIndex, Matrix<double> output) |
|||
{ |
|||
if (itemIndex == 0) |
|||
{ |
|||
output[0, 0] = 0; |
|||
output[0, 1] = 0; |
|||
output[1, 0] = 0; |
|||
output[1, 1] = 10 - 6 * x[1]; |
|||
} |
|||
else |
|||
{ |
|||
output[0, 0] = 0; |
|||
output[0, 1] = 0; |
|||
output[1, 0] = 0; |
|||
output[1, 1] = 2 + 6 * x[1]; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,178 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using MathNet.Numerics.LinearAlgebra; |
|||
|
|||
namespace MathNet.Numerics.UnitTests.OptimizationTests.TestFunctions |
|||
{ |
|||
public class HelicalValleyFunction : BaseTestFunction |
|||
{ |
|||
public static IEnumerable<TestCase> TestCases |
|||
{ |
|||
get |
|||
{ |
|||
yield return new TestCase() |
|||
{ |
|||
Function = new HelicalValleyFunction(), |
|||
InitialGuess = new double[] { -1, 0, 0 }, |
|||
MinimalValue = 0, |
|||
MinimizingPoint = new double[] { 1, 0, 0 }, |
|||
CaseName = "unbounded" |
|||
}; |
|||
yield return new TestCase() |
|||
{ |
|||
Function = new HelicalValleyFunction(), |
|||
InitialGuess = new double[] { -1, 0, 0 }, |
|||
MinimalValue = 0, |
|||
MinimizingPoint = new double[] { 1, 0, 0 }, |
|||
LowerBound = new double[] { -1000, -1000, -1000 }, |
|||
UpperBound = new double[] { 1000, 1000, 1000 }, |
|||
CaseName = "loose bounds" |
|||
}; |
|||
yield return new TestCase() |
|||
{ |
|||
Function = new HelicalValleyFunction(), |
|||
InitialGuess = new double[] { -1, 0, 0 }, |
|||
MinimalValue = 0.99042212, |
|||
LowerBound = new double[] { -100, -1, -1 }, |
|||
UpperBound = new double[] { 0.8, 1, 1 }, |
|||
CaseName = "tight bounds" |
|||
}; |
|||
} |
|||
} |
|||
|
|||
public override string Description |
|||
{ |
|||
get |
|||
{ |
|||
return "Helical valley fun (MGH #7)"; |
|||
} |
|||
} |
|||
|
|||
public override int ItemDimension |
|||
{ |
|||
get |
|||
{ |
|||
return 3; |
|||
} |
|||
} |
|||
|
|||
public override int ParameterDimension |
|||
{ |
|||
get |
|||
{ |
|||
return 3; |
|||
} |
|||
} |
|||
|
|||
public override void ItemGradientByRef(Vector<double> x, int itemIndex, Vector<double> output) |
|||
{ |
|||
switch (itemIndex) |
|||
{ |
|||
case 0: |
|||
output[0] = -100 * theta10(x[0], x[1]); |
|||
output[1] = -100 * theta01(x[0], x[1]); |
|||
output[2] = 10; |
|||
break; |
|||
case 1: |
|||
output[0] = (10 * x[0]) / Math.Sqrt(x[0]*x[0] + x[1]*x[1]); |
|||
output[1] = (10 * x[1]) / Math.Sqrt(x[0]*x[0] + x[1]*x[1]); |
|||
output[2] = 0; |
|||
break; |
|||
case 2: |
|||
output[0] = 0; |
|||
output[1] = 0; |
|||
output[2] = 1; |
|||
break; |
|||
default: |
|||
throw new ArgumentException("itemIndex must be <= 2"); |
|||
} |
|||
} |
|||
|
|||
public override void ItemHessianByRef(Vector<double> x, int itemIndex, Matrix<double> output) |
|||
{ |
|||
switch (itemIndex) |
|||
{ |
|||
case 0: |
|||
output[0, 0] = -100 * theta20(x[0], x[1]); |
|||
output[0, 1] = -100 * theta11(x[0], x[1]); |
|||
output[0, 2] = 0; |
|||
output[1, 0] = -100 * theta11(x[0], x[1]); |
|||
output[1, 1] = -100 * theta02(x[0], x[1]); |
|||
output[1, 2] = 0; |
|||
output[2, 0] = 0; |
|||
output[2, 1] = 0; |
|||
output[2, 2] = 0; |
|||
break; |
|||
case 1: |
|||
output[0, 0] = (10 * x[1]*x[1]) / Math.Pow(x[0]*x[0] + x[1]*x[1],1.5); |
|||
output[0, 1] = (-10 * x[0] * x[1]) / Math.Pow(x[0]*x[0] + x[1]*x[1],1.5); |
|||
output[0, 2] = 0; |
|||
output[1, 0] = (-10 * x[0] * x[1]) / Math.Pow(x[0] * x[0] + x[1] * x[1], 1.5); |
|||
output[1, 1] = (10 * x[0]*x[0]) / Math.Pow(x[0] * x[0] + x[1] * x[1], 1.5); |
|||
output[1, 2] = 0; |
|||
output[2, 0] = 0; |
|||
output[2, 1] = 0; |
|||
output[2, 2] = 0; |
|||
break; |
|||
case 2: |
|||
for (int ii = 0; ii < 2; ++ii) |
|||
for (int jj = 0; jj < 2; ++jj) |
|||
output[ii, jj] = 0; |
|||
break; |
|||
default: |
|||
throw new ArgumentException("itemIndex must be <= 2"); |
|||
} |
|||
} |
|||
|
|||
private static double theta(double x1, double x2) |
|||
{ |
|||
if (x1 >= 0) |
|||
return 0.5 * Math.Atan(x2 / x1) / Math.PI; |
|||
else |
|||
return 0.5 * Math.Atan(x2 / x1) / Math.PI + 0.5; |
|||
} |
|||
|
|||
private static double theta10(double x1, double x2) |
|||
{ |
|||
return -(x2 / (2 * Math.PI * Math.Pow(x1,2) + 2 * Math.PI * Math.Pow(x2,2))); |
|||
} |
|||
|
|||
private static double theta01(double x1, double x2) |
|||
{ |
|||
return x1 / (2 * Math.PI * x1*x1 + 2 * Math.PI * x2*x2); |
|||
} |
|||
|
|||
private static double theta20(double x1,double x2) |
|||
{ |
|||
return (x1 * x2) / (Math.PI * Math.Pow(x1 * x1 + x2 * x2, 2)); |
|||
} |
|||
|
|||
private static double theta11(double x1, double x2) |
|||
{ |
|||
return (-x1 * x1 + x2 * x2) / (2 * Math.PI * Math.Pow(x1 * x1 + x2 * x2, 2)); |
|||
} |
|||
|
|||
private static double theta02(double x1, double x2) |
|||
{ |
|||
return -((x1 * x2) / (Math.PI * Math.Pow(x1*x1 + x2*x2, 2))); |
|||
} |
|||
|
|||
public override double ItemValue(Vector<double> x, int itemIndex) |
|||
{ |
|||
switch (itemIndex) |
|||
{ |
|||
case 0: |
|||
return 10 * (x[2] - 10 * theta(x[0], x[1])); |
|||
case 1: |
|||
return 10 * (Math.Sqrt(x[0] * x[0] + x[1] * x[1]) - 1); |
|||
case 2: |
|||
return x[2]; |
|||
default: |
|||
throw new ArgumentException("itemIndex must be <= 2"); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,69 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using MathNet.Numerics.LinearAlgebra; |
|||
using DenseVector = MathNet.Numerics.LinearAlgebra.Double.DenseVector; |
|||
|
|||
namespace MathNet.Numerics.UnitTests.OptimizationTests.TestFunctions |
|||
{ |
|||
public class TestCase |
|||
{ |
|||
public string CaseName; |
|||
public ITestFunction Function; |
|||
public DenseVector InitialGuess; |
|||
public DenseVector LowerBound; |
|||
public DenseVector UpperBound; |
|||
public double MinimalValue; |
|||
public DenseVector MinimizingPoint; |
|||
|
|||
public bool IsBounded |
|||
{ |
|||
get |
|||
{ |
|||
return this.LowerBound != null && this.UpperBound != null; |
|||
} |
|||
} |
|||
|
|||
public bool IsUnbounded |
|||
{ |
|||
get |
|||
{ |
|||
return this.IsUnboundedOverride ?? this.LowerBound == null || this.UpperBound == null; |
|||
} |
|||
} |
|||
|
|||
public bool? IsUnboundedOverride; |
|||
|
|||
public string FullName |
|||
{ |
|||
get |
|||
{ |
|||
return $"{this.Function.Description} {this.CaseName}"; |
|||
} |
|||
} |
|||
} |
|||
|
|||
public interface ITestFunction |
|||
{ |
|||
string Description { get; } |
|||
int ParameterDimension { get; } |
|||
int ItemDimension { get; } |
|||
|
|||
double ItemValue(Vector<double> x, int itemIndex); |
|||
Vector<double> ItemGradient(Vector<double> x, int itemIndex); |
|||
void ItemGradientByRef(Vector<double> x, int itemIndex, Vector<double> output); |
|||
Matrix<double> ItemHessian(Vector<double> x, int itemIndex); |
|||
void ItemHessianByRef(Vector<double> x, int itemIndex, Matrix<double> output); |
|||
|
|||
Matrix<double> Jacobian(Vector<double> x); |
|||
void JacobianbyRef(Vector<double> x, Matrix<double> output); |
|||
|
|||
double SsqValue(Vector<double> x); |
|||
Vector<double> SsqGradient(Vector<double> x); |
|||
void SsqGradientByRef(Vector<double> x, Vector<double> output); |
|||
Matrix<double> SsqHessian(Vector<double> x); |
|||
void SsqHessianByRef(Vector<double> x, Matrix<double> output); |
|||
} |
|||
} |
|||
@ -0,0 +1,102 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using MathNet.Numerics.LinearAlgebra; |
|||
|
|||
namespace MathNet.Numerics.UnitTests.OptimizationTests.TestFunctions |
|||
{ |
|||
public class JennrichAndSampsonFunction : BaseTestFunction |
|||
{ |
|||
private readonly int _m; |
|||
|
|||
public JennrichAndSampsonFunction(int itemDimension) |
|||
{ |
|||
if (itemDimension < 2) |
|||
throw new ArgumentException("itemDimension must be at least 2."); |
|||
_m = itemDimension; |
|||
} |
|||
|
|||
public static IEnumerable<TestCase> TestCases |
|||
{ |
|||
get |
|||
{ |
|||
yield return new TestCase() |
|||
{ |
|||
Function = new JennrichAndSampsonFunction(10), |
|||
InitialGuess = new double[] { 0.3, 0.4 }, |
|||
MinimalValue = 124.362, |
|||
MinimizingPoint = new double[] { 0.2578, 0.2578 }, |
|||
CaseName = "unbounded" |
|||
}; |
|||
//yield return new TestCase()
|
|||
//{
|
|||
// Function = new JennrichAndSampsonFunction(10),
|
|||
// LowerBound = new double[] { 0.6, 0.5 },
|
|||
// UpperBound = new double[] { 10, 50 },
|
|||
// StartPoint = new double[] { 1.0, 1.0 },
|
|||
// MinimizingInput = null,
|
|||
// MinimizingValue = 0,
|
|||
// CaseName = "tight bounds"
|
|||
//};
|
|||
yield return new TestCase() |
|||
{ |
|||
Function = new JennrichAndSampsonFunction(10), |
|||
LowerBound = new double[] { -50, -50 }, |
|||
UpperBound = new double[] { 50, 50 }, |
|||
InitialGuess = new double[] { 0.3, 0.4 }, |
|||
MinimizingPoint = null, |
|||
MinimalValue = 0, |
|||
CaseName = "loose bounds" |
|||
}; |
|||
} |
|||
} |
|||
|
|||
public override string Description |
|||
{ |
|||
get |
|||
{ |
|||
return $"Jennrich & Sampson fun (MGH #6) (n={this.ItemDimension})"; |
|||
} |
|||
} |
|||
|
|||
public override int ItemDimension |
|||
{ |
|||
get |
|||
{ |
|||
return _m; |
|||
} |
|||
} |
|||
|
|||
public override int ParameterDimension |
|||
{ |
|||
get |
|||
{ |
|||
return 2; |
|||
} |
|||
} |
|||
|
|||
public override void ItemGradientByRef(Vector<double> x, int itemIndex, Vector<double> output) |
|||
{ |
|||
int ii = itemIndex + 1; |
|||
output[0] = -(Math.Exp(ii * x[0]) * ii); |
|||
output[1] = -(Math.Exp(ii * x[1]) * ii); |
|||
} |
|||
|
|||
public override void ItemHessianByRef(Vector<double> x, int itemIndex, Matrix<double> output) |
|||
{ |
|||
int ii = itemIndex + 1; |
|||
output[0, 0] = -(Math.Exp(ii * x[0]) * ii*ii); |
|||
output[0, 1] = 0; |
|||
output[1, 0] = 0; |
|||
output[1, 1] = -(Math.Exp(ii * x[1]) * ii*ii); |
|||
} |
|||
|
|||
public override double ItemValue(Vector<double> x, int itemIndex) |
|||
{ |
|||
int ii = itemIndex + 1; |
|||
return 2 + 2 * ii - (Math.Exp(ii * x[0]) + Math.Exp(ii * x[1])); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,99 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using MathNet.Numerics.LinearAlgebra; |
|||
|
|||
namespace MathNet.Numerics.UnitTests.OptimizationTests.TestFunctions |
|||
{ |
|||
public class MeyerFunction : BaseTestFunction |
|||
{ |
|||
public static IEnumerable<TestCase> TestCases |
|||
{ |
|||
get |
|||
{ |
|||
yield return new TestCase() |
|||
{ |
|||
Function = new MeyerFunction(), |
|||
InitialGuess = new double[] { 0.02, 4000, 250 }, |
|||
MinimalValue = 87.9458, |
|||
MinimizingPoint = null, |
|||
CaseName = "unbounded" |
|||
}; |
|||
yield return new TestCase() |
|||
{ |
|||
Function = new MeyerFunction(), |
|||
InitialGuess = new double[] { 0.02, 4000, 250 }, |
|||
MinimalValue = 87.9458, |
|||
MinimizingPoint = null, |
|||
LowerBound = new double[] { -1e6, -1e6, -1e6 }, |
|||
UpperBound = new double[] { 1e6, 1e6, 1e6 }, |
|||
CaseName = "loose bounds" |
|||
}; |
|||
} |
|||
} |
|||
|
|||
public MeyerFunction() { } |
|||
|
|||
public override string Description |
|||
{ |
|||
get |
|||
{ |
|||
return "Meyer fun (MGH #10)"; |
|||
} |
|||
} |
|||
|
|||
public override int ItemDimension |
|||
{ |
|||
get |
|||
{ |
|||
return 16; |
|||
} |
|||
} |
|||
|
|||
public override int ParameterDimension |
|||
{ |
|||
get |
|||
{ |
|||
return 3; |
|||
} |
|||
} |
|||
|
|||
public override void ItemGradientByRef(Vector<double> x, int itemIndex, Vector<double> output) |
|||
{ |
|||
int ii = itemIndex + 1; |
|||
output[0] = Math.Exp(x[1] / (45.0 + 5 * ii + x[2])); |
|||
output[1] = (Math.Exp(x[1] / (45.0 + 5 * ii + x[2])) * x[0]) / (45 + 5 * ii + x[2]); |
|||
output[2] = -(Math.Exp(x[1] / (45.0 + 5 * ii + x[2])) * x[0] * x[1]) / Math.Pow(45 + 5 * ii + x[2], 2); |
|||
|
|||
} |
|||
|
|||
public override void ItemHessianByRef(Vector<double> x, int itemIndex, Matrix<double> output) |
|||
{ |
|||
var ii = itemIndex + 1; |
|||
|
|||
var t0 = (45.0 + 5 * ii + x[2]); |
|||
var t1 = Math.Exp(x[1] / t0); |
|||
|
|||
output[0, 0] = 0; |
|||
output[0, 1] = t1 / t0; |
|||
output[0, 2] = -t1 * x[1] / Math.Pow(t0, 2); |
|||
output[1, 0] = t1 / t0; |
|||
output[1, 1] = t1 * x[0] / Math.Pow(t0, 2); |
|||
output[1, 2] = -t1 * x[0] * (t0 + x[1]) / Math.Pow(t0, 3); |
|||
output[2, 0] = -t1 * x[1] / Math.Pow(t0, 2); |
|||
output[2, 1] = -t1 * x[0] * (t0 + x[1]) / Math.Pow(t0, 3); |
|||
output[2, 2] = t1 * x[0] * x[1] * (2*t0 + x[1]) / Math.Pow(t0, 4); |
|||
} |
|||
|
|||
private static readonly double[] y = { 34780, 28610, 23650, 19630, 16370, 13720, 11540, 9744, 8261, 7030, 6005, 5147, 4427, 3820, 3307, 2872 }; |
|||
|
|||
public override double ItemValue(Vector<double> x, int itemIndex) |
|||
{ |
|||
var ii = itemIndex + 1; |
|||
var t = 45.0 + 5 * ii; |
|||
return x[0] * Math.Exp(x[1] / (t + x[2])) - y[itemIndex]; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,111 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using MathNet.Numerics.LinearAlgebra; |
|||
using MathNet.Numerics.LinearAlgebra.Double; |
|||
|
|||
namespace MathNet.Numerics.UnitTests.OptimizationTests.TestFunctions |
|||
{ |
|||
public class PowellBadlyScaledFunction : BaseTestFunction |
|||
{ |
|||
public static IEnumerable<TestCase> TestCases |
|||
{ |
|||
get |
|||
{ |
|||
yield return new TestCase() |
|||
{ |
|||
Function = new PowellBadlyScaledFunction(), |
|||
InitialGuess = new double[] { 0, 1 }, |
|||
MinimizingPoint = new double[] { 1.098e-5, 9.106 }, |
|||
MinimalValue = 0, |
|||
CaseName = "unbounded" |
|||
}; |
|||
yield return new TestCase() |
|||
{ |
|||
Function = new PowellBadlyScaledFunction(), |
|||
InitialGuess = new double[] { 0, 1 }, |
|||
MinimizingPoint = new double[] { 1.098e-5, 9.106 }, |
|||
MinimalValue = 0, |
|||
LowerBound = new double[] { -1000, -1000 }, |
|||
UpperBound = new double[] { 1000, 1000 }, |
|||
CaseName = "loose bounds" |
|||
}; |
|||
yield return new TestCase() |
|||
{ |
|||
Function = new PowellBadlyScaledFunction(), |
|||
LowerBound = new double[] { 0, 1 }, |
|||
UpperBound = new double[] { 1, 9 }, |
|||
InitialGuess = new double[] { 0, 1 }, |
|||
MinimalValue = 0.15125900e-9, |
|||
CaseName = "tight bounds" |
|||
}; |
|||
} |
|||
} |
|||
|
|||
public override string Description |
|||
{ |
|||
get |
|||
{ |
|||
return "Powell badly scaled fun (MGH #3)"; |
|||
} |
|||
} |
|||
|
|||
public override int ItemDimension |
|||
{ |
|||
get |
|||
{ |
|||
return 2; |
|||
} |
|||
} |
|||
|
|||
public override int ParameterDimension |
|||
{ |
|||
get |
|||
{ |
|||
return 2; |
|||
} |
|||
} |
|||
|
|||
public override void ItemGradientByRef(Vector<double> x, int itemIndex, Vector<double> output) |
|||
{ |
|||
if (itemIndex == 0) |
|||
{ |
|||
output[0] = 10000 * x[1]; |
|||
output[1] = 10000 * x[0]; |
|||
} |
|||
else if (itemIndex == 1) |
|||
{ |
|||
output[0] = -Math.Exp(-x[0]); |
|||
output[1] = -Math.Exp(-x[1]); |
|||
} |
|||
} |
|||
|
|||
public override void ItemHessianByRef(Vector<double> x, int itemIndex, Matrix<double> output) |
|||
{ |
|||
if (itemIndex == 0) |
|||
{ |
|||
output[0, 0] = 0; |
|||
output[0, 1] = 10000; |
|||
output[1, 0] = 10000; |
|||
output[1, 1] = 0; |
|||
} |
|||
else |
|||
{ |
|||
output[0, 0] = Math.Exp(-x[0]); |
|||
output[0, 1] = 0; |
|||
output[1, 0] = 0; |
|||
output[1,1] = Math.Exp(-x[1]); |
|||
} |
|||
} |
|||
|
|||
public override double ItemValue(Vector<double> x, int itemIndex) |
|||
{ |
|||
if (itemIndex == 0) |
|||
return 10000.0 * x[0] * x[1] - 1; |
|||
else |
|||
return Math.Exp(-x[0]) + Math.Exp(-x[1]) - 1.0001; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,141 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using MathNet.Numerics.LinearAlgebra; |
|||
|
|||
namespace MathNet.Numerics.UnitTests.OptimizationTests.TestFunctions |
|||
{ |
|||
public class PowellSingularFunction : BaseTestFunction |
|||
{ |
|||
public static IEnumerable<TestCase> TestCases |
|||
{ |
|||
get |
|||
{ |
|||
yield return new TestCase() |
|||
{ |
|||
Function = new PowellSingularFunction(), |
|||
InitialGuess = new double[] { 3, -1, 0, 1 }, |
|||
MinimalValue = 0, |
|||
MinimizingPoint = new double[] {0,0,0,0}, |
|||
CaseName = "unbounded" |
|||
}; |
|||
yield return new TestCase() |
|||
{ |
|||
Function = new PowellSingularFunction(), |
|||
InitialGuess = new double[] { 3, -1, 0, 1 }, |
|||
MinimalValue = 0, |
|||
MinimizingPoint = new double[] { 0, 0, 0, 0 }, |
|||
LowerBound = new double[] {-1000, -1000, -1000, -1000}, |
|||
UpperBound = new double[] { 1000, 1000, 1000, 1000 }, |
|||
CaseName = "loose bounds" |
|||
}; |
|||
} |
|||
} |
|||
|
|||
|
|||
public PowellSingularFunction() { } |
|||
|
|||
public override string Description |
|||
{ |
|||
get |
|||
{ |
|||
return "Powell singular fun (MGH #13)"; |
|||
} |
|||
} |
|||
|
|||
public override int ItemDimension |
|||
{ |
|||
get |
|||
{ |
|||
return 4; |
|||
} |
|||
} |
|||
|
|||
public override int ParameterDimension |
|||
{ |
|||
get |
|||
{ |
|||
return 4; |
|||
} |
|||
} |
|||
|
|||
public override void ItemGradientByRef(Vector<double> x, int itemIndex, Vector<double> output) |
|||
{ |
|||
switch (itemIndex) |
|||
{ |
|||
case 0: |
|||
output[0] = 1; |
|||
output[1] = 10; |
|||
output[2] = 0; |
|||
output[3] = 0; |
|||
break; |
|||
case 1: |
|||
output[0] = 0; |
|||
output[1] = 0; |
|||
output[2] = Math.Sqrt(5); |
|||
output[3] = -Math.Sqrt(5); |
|||
break; |
|||
case 2: |
|||
output[0] = 0; |
|||
output[1] = 2*(x[1]-2*x[2]); |
|||
output[2] = -4*x[1] + 8*x[2]; |
|||
output[3] = 0; |
|||
break; |
|||
case 3: |
|||
output[0] = 2*Math.Sqrt(10)*(x[0] - x[3]); |
|||
output[1] = 0; |
|||
output[2] = 0; |
|||
output[3] = -2*Math.Sqrt(10)*(x[0] - x[3]); |
|||
break; |
|||
default: |
|||
throw new ArgumentException("itemIndex must be <= 3"); |
|||
} |
|||
} |
|||
|
|||
public override void ItemHessianByRef(Vector<double> x, int itemIndex, Matrix<double> output) |
|||
{ |
|||
for (int ii = 0; ii < 4; ++ii) |
|||
for (int jj = 0; jj < 4; ++jj) |
|||
output[ii, jj] = 0; |
|||
switch(itemIndex) |
|||
{ |
|||
case 0: |
|||
case 1: |
|||
break; |
|||
case 2: |
|||
output[1, 1] = 2; |
|||
output[1, 2] = -4; |
|||
output[2, 1] = -4; |
|||
output[2, 2] = 8; |
|||
break; |
|||
case 3: |
|||
output[0, 0] = 2 * Math.Sqrt(10); |
|||
output[0, 3] = -2 * Math.Sqrt(10); |
|||
output[3, 0] = -2 * Math.Sqrt(10); |
|||
output[3, 3] = 2 * Math.Sqrt(10); |
|||
break; |
|||
default: |
|||
throw new ArgumentException("itemIndex must be <= 3"); |
|||
} |
|||
} |
|||
|
|||
public override double ItemValue(Vector<double> x, int itemIndex) |
|||
{ |
|||
switch (itemIndex) |
|||
{ |
|||
case 0: |
|||
return x[0] + 10 * x[1]; |
|||
case 1: |
|||
return Math.Sqrt(5) * (x[2] - x[3]); |
|||
case 2: |
|||
return Math.Pow(x[1] - 2 * x[2], 2); |
|||
case 3: |
|||
return Math.Sqrt(10.0) * Math.Pow(x[0] - x[3], 2); |
|||
default: |
|||
throw new ArgumentException("itemIndex must be <= 3"); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,156 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using MathNet.Numerics.LinearAlgebra; |
|||
|
|||
namespace MathNet.Numerics.UnitTests.OptimizationTests.TestFunctions |
|||
{ |
|||
public class RosenbrockFunction2 : BaseTestFunction |
|||
{ |
|||
public static IEnumerable<TestCase> TestCases |
|||
{ |
|||
get |
|||
{ |
|||
yield return new TestCase() |
|||
{ |
|||
Function = new RosenbrockFunction2(), |
|||
InitialGuess = new double[] { -1.2, 1 }, |
|||
MinimizingPoint = new double[] { 1, 1 }, |
|||
MinimalValue = 0, |
|||
LowerBound = new double[] { -1000, -1000 }, |
|||
UpperBound = new double[] { 1000, 1000 }, |
|||
CaseName = "hard start", |
|||
IsUnboundedOverride = true |
|||
}; |
|||
yield return new TestCase() |
|||
{ |
|||
Function = new RosenbrockFunction2(), |
|||
InitialGuess = new double[] { 1.2, 1.2 }, |
|||
MinimizingPoint = new double[] { 1, 1 }, |
|||
MinimalValue = 0, |
|||
LowerBound = new double[] { -5, -5 }, |
|||
UpperBound = new double[] { 5, 5 }, |
|||
CaseName = "easy start" |
|||
}; |
|||
yield return new TestCase() |
|||
{ |
|||
Function = new RosenbrockFunction2(), |
|||
InitialGuess = new double[] { -0.9, -0.5 }, |
|||
MinimizingPoint = new double[] { 1, 1 }, |
|||
MinimalValue = 0, |
|||
LowerBound = new double[] { -5, -5 }, |
|||
UpperBound = new double[] { 5, 5 }, |
|||
CaseName = "Overton start", |
|||
IsUnboundedOverride = true |
|||
}; |
|||
yield return new TestCase() |
|||
{ |
|||
Function = new RosenbrockFunction2(), |
|||
InitialGuess = new double[] { 1.2, 1.2 }, |
|||
MinimizingPoint = new double[] { 1, 1 }, |
|||
MinimalValue = 0, |
|||
LowerBound = new double[] { 1, -5 }, |
|||
UpperBound = new double[] { 5, 5 }, |
|||
CaseName = "easy one active bound" |
|||
}; |
|||
yield return new TestCase() |
|||
{ |
|||
Function = new RosenbrockFunction2(), |
|||
InitialGuess = new double[] { 1.2, 1.2 }, |
|||
MinimizingPoint = new double[] { 1, 1 }, |
|||
MinimalValue = 0, |
|||
LowerBound = new double[] { 1, 1 }, |
|||
UpperBound = new double[] { 5, 5 }, |
|||
CaseName = "easy two active bounds" |
|||
}; |
|||
yield return new TestCase() |
|||
{ |
|||
Function = new RosenbrockFunction2(), |
|||
InitialGuess = new double[] { 2.5, 2.5 }, |
|||
MinimizingPoint = new double[] { 2, 4 }, |
|||
MinimalValue = 1, |
|||
LowerBound = new double[] { 2, 2 }, |
|||
UpperBound = new double[] { 5, 5 }, |
|||
CaseName = "min on lower bound, not local" |
|||
}; |
|||
yield return new TestCase() |
|||
{ |
|||
Function = new RosenbrockFunction2(), |
|||
InitialGuess = new double[] { -0.9, -0.5 }, |
|||
MinimizingPoint = new double[] { 0.5, 0.25 }, |
|||
MinimalValue = 0.25, |
|||
LowerBound = new double[] { -2, -2 }, |
|||
UpperBound = new double[] { 0.5, 0.5 }, |
|||
CaseName = "min on upper bound, not local" |
|||
}; |
|||
|
|||
|
|||
} |
|||
} |
|||
public RosenbrockFunction2() { } |
|||
|
|||
public override string Description |
|||
{ |
|||
get |
|||
{ |
|||
return "Rosenbrock fun (MGH #1)"; |
|||
} |
|||
} |
|||
|
|||
public override int ItemDimension |
|||
{ |
|||
get |
|||
{ |
|||
return 2; |
|||
} |
|||
} |
|||
|
|||
public override int ParameterDimension |
|||
{ |
|||
get |
|||
{ |
|||
return 2; |
|||
} |
|||
} |
|||
|
|||
public override void ItemGradientByRef(Vector<double> x, int itemIndex, Vector<double> output) |
|||
{ |
|||
if (itemIndex == 0) |
|||
{ |
|||
output[0] = -20 * x[0]; |
|||
output[1] = 10; |
|||
} else |
|||
{ |
|||
output[0] = -1; |
|||
output[1] = 0; |
|||
} |
|||
} |
|||
|
|||
public override void ItemHessianByRef(Vector<double> x, int itemIndex, Matrix<double> output) |
|||
{ |
|||
if (itemIndex == 0) |
|||
{ |
|||
output[0, 0] = -20; |
|||
output[0, 1] = 0; |
|||
output[1, 0] = 0; |
|||
output[1, 1] = 0; |
|||
} else |
|||
{ |
|||
output[0, 0] = 0; |
|||
output[0, 1] = 0; |
|||
output[1, 0] = 0; |
|||
output[1, 1] = 0; |
|||
} |
|||
} |
|||
|
|||
public override double ItemValue(Vector<double> x, int itemIndex) |
|||
{ |
|||
if (itemIndex == 0) |
|||
return 10 * (x[1] - x[0] * x[0]); |
|||
else |
|||
return 1 - x[0]; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,164 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using MathNet.Numerics.LinearAlgebra; |
|||
|
|||
namespace MathNet.Numerics.UnitTests.OptimizationTests.TestFunctions |
|||
{ |
|||
public class WoodFunction : BaseTestFunction |
|||
{ |
|||
public static IEnumerable<TestCase> TestCases |
|||
{ |
|||
get |
|||
{ |
|||
yield return new TestCase() |
|||
{ |
|||
Function = new WoodFunction(), |
|||
InitialGuess = new double[] { -3, -1, -3, -1 }, |
|||
MinimalValue = 0, |
|||
MinimizingPoint = new double[] { 1, 1, 1, 1 }, |
|||
CaseName = "unbounded" |
|||
}; |
|||
yield return new TestCase() |
|||
{ |
|||
Function = new WoodFunction(), |
|||
InitialGuess = new double[] { -3, -1, -3, -1 }, |
|||
MinimalValue = 0, |
|||
MinimizingPoint = new double[] { 1, 1, 1, 1 }, |
|||
LowerBound = new double[] { -1000, -1000, -1000, -1000 }, |
|||
UpperBound = new double[] { 1000, 1000, 1000, 1000 }, |
|||
CaseName = "loose bounds" |
|||
}; |
|||
yield return new TestCase() |
|||
{ |
|||
Function = new WoodFunction(), |
|||
InitialGuess = new double[] { -3, -1, -3, -1 }, |
|||
MinimalValue = 1.5567008, |
|||
MinimizingPoint = null, |
|||
LowerBound = new double[] { -100, -100, -100, -100 }, |
|||
UpperBound = new double[] { 0, 10, 100, 100 }, |
|||
CaseName = "tight bounds" |
|||
}; |
|||
} |
|||
} |
|||
|
|||
public WoodFunction() { } |
|||
|
|||
public override string Description |
|||
{ |
|||
get |
|||
{ |
|||
return "Wood fun (MGH #14)"; |
|||
} |
|||
} |
|||
|
|||
public override int ItemDimension |
|||
{ |
|||
get |
|||
{ |
|||
return 6; |
|||
} |
|||
} |
|||
|
|||
public override int ParameterDimension |
|||
{ |
|||
get |
|||
{ |
|||
return 4; |
|||
} |
|||
} |
|||
|
|||
public override void ItemGradientByRef(Vector<double> x, int itemIndex, Vector<double> output) |
|||
{ |
|||
switch (itemIndex) |
|||
{ |
|||
case 0: |
|||
output[0] = -20 * x[0]; |
|||
output[1] = 10; |
|||
output[2] = 0; |
|||
output[3] = 0; |
|||
break; |
|||
case 1: |
|||
output[0] = -1; |
|||
output[1] = 0; |
|||
output[2] = 0; |
|||
output[3] = 0; |
|||
break; |
|||
case 2: |
|||
output[0] = 0; |
|||
output[1] = 0; |
|||
output[2] = -6 * Math.Sqrt(10) * x[2]; |
|||
output[3] = 3 * Math.Sqrt(10); |
|||
break; |
|||
case 3: |
|||
output[0] = 0; |
|||
output[1] = 0; |
|||
output[2] = -1; |
|||
output[3] = 0; |
|||
break; |
|||
case 4: |
|||
output[0] = 0; |
|||
output[1] = Math.Sqrt(10); |
|||
output[2] = 0; |
|||
output[3] = Math.Sqrt(10); |
|||
break; |
|||
case 5: |
|||
output[0] = 0; |
|||
output[1] = 1.0 / Math.Sqrt(10); |
|||
output[2] = 0; |
|||
output[3] = -1.0 / Math.Sqrt(10); |
|||
break; |
|||
default: |
|||
throw new ArgumentException("itemIndex must be <= 5"); |
|||
} |
|||
} |
|||
|
|||
public override void ItemHessianByRef(Vector<double> x, int itemIndex, Matrix<double> output) |
|||
{ |
|||
for (int ii = 0; ii < 4; ++ii) |
|||
for (int jj = 0; jj < 4; ++jj) |
|||
output[ii, jj] = 0; |
|||
switch (itemIndex) |
|||
{ |
|||
case 0: |
|||
output[0, 0] = -20; |
|||
break; |
|||
case 1: |
|||
break; |
|||
case 2: |
|||
output[2, 2] = -6 * Math.Sqrt(10); |
|||
break; |
|||
case 3: |
|||
case 4: |
|||
case 5: |
|||
break; |
|||
default: |
|||
throw new ArgumentException("itemIndex must be <= 5"); |
|||
|
|||
} |
|||
} |
|||
|
|||
public override double ItemValue(Vector<double> x, int itemIndex) |
|||
{ |
|||
switch (itemIndex) |
|||
{ |
|||
case 0: |
|||
return 10 * (x[1] - x[0] * x[0]); |
|||
case 1: |
|||
return 1 - x[0]; |
|||
case 2: |
|||
return Math.Sqrt(90) * (x[3] - x[2] * x[2]); |
|||
case 3: |
|||
return 1 - x[2]; |
|||
case 4: |
|||
return Math.Sqrt(10) * (x[1] + x[3] - 2); |
|||
case 5: |
|||
return (x[1] - x[3]) / Math.Sqrt(10); |
|||
default: |
|||
throw new ArgumentException("itemIndex must be <= 5"); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue