diff --git a/src/Numerics/FindMinimum.cs b/src/Numerics/FindMinimum.cs index 28cfc154..b34c824e 100644 --- a/src/Numerics/FindMinimum.cs +++ b/src/Numerics/FindMinimum.cs @@ -30,7 +30,6 @@ using System; using MathNet.Numerics.LinearAlgebra; using MathNet.Numerics.Optimization; -using MathNet.Numerics.Optimization.ObjectiveFunctions; namespace MathNet.Numerics { @@ -42,7 +41,7 @@ namespace MathNet.Numerics /// public static double OfScalarFunctionConstrained(Func function, double lowerBound, double upperBound, double tolerance=1e-5, int maxIterations=1000) { - var objective = new SimpleObjectiveFunction1D(function); + var objective = ObjectiveFunction.ScalarValue(function); var result = GoldenSectionMinimizer.Minimum(objective, lowerBound, upperBound, tolerance, maxIterations); return result.MinimizingPoint; } @@ -66,7 +65,7 @@ namespace MathNet.Numerics public static Vector OfFunctionConstrained(Func, double> function, Vector lowerBound, Vector upperBound, Vector initialGuess, double gradientTolerance=1e-5, double parameterTolerance=1e-5, double functionProgressTolerance=1e-5, int maxIterations=1000) { var objective = ObjectiveFunction.Value(function); - var objectiveWithGradient = new ForwardDifferenceGradientObjectiveFunction(objective, lowerBound, upperBound); + var objectiveWithGradient = new Optimization.ObjectiveFunctions.ForwardDifferenceGradientObjectiveFunction(objective, lowerBound, upperBound); var algorithm = new BfgsBMinimizer(gradientTolerance, parameterTolerance, functionProgressTolerance, maxIterations); var result = algorithm.FindMinimum(objectiveWithGradient, lowerBound, upperBound, initialGuess); return result.MinimizingPoint; @@ -75,6 +74,7 @@ namespace MathNet.Numerics /// /// Find vector x that minimizes the function f(x) using the Broyden–Fletcher–Goldfarb–Shanno (BFGS) algorithm. /// For more options and diagnostics consider to use directly. + /// An alternative routine using conjugate gradients (CG) is available in . /// public static Vector OfFunctionGradient(Func, double> function, Func, Vector> gradient, Vector initialGuess, double gradientTolerance=1e-5, double parameterTolerance=1e-5, double functionProgressTolerance=1e-5, int maxIterations=1000) { @@ -87,6 +87,7 @@ namespace MathNet.Numerics /// /// Find vector x that minimizes the function f(x) using the Broyden–Fletcher–Goldfarb–Shanno (BFGS) algorithm. /// For more options and diagnostics consider to use directly. + /// An alternative routine using conjugate gradients (CG) is available in . /// public static Vector OfFunctionGradient(Func, Tuple>> functionGradient, Vector initialGuess, double gradientTolerance=1e-5, double parameterTolerance=1e-5, double functionProgressTolerance=1e-5, int maxIterations=1000) { diff --git a/src/Numerics/Numerics.csproj b/src/Numerics/Numerics.csproj index 8d8a30ad..fea15244 100644 --- a/src/Numerics/Numerics.csproj +++ b/src/Numerics/Numerics.csproj @@ -123,8 +123,10 @@ + + @@ -267,10 +269,9 @@ - + - diff --git a/src/Numerics/Optimization/GoldenSectionMinimizer.cs b/src/Numerics/Optimization/GoldenSectionMinimizer.cs index b784dcc9..ca7dfd98 100644 --- a/src/Numerics/Optimization/GoldenSectionMinimizer.cs +++ b/src/Numerics/Optimization/GoldenSectionMinimizer.cs @@ -48,12 +48,12 @@ namespace MathNet.Numerics.Optimization UpperExpansionFactor = upperExpansionFactor; } - public MinimizationResult1D FindMinimum(IObjectiveFunction1D objective, double lowerBound, double upperBound) + public ScalarMinimizationResult FindMinimum(IScalarObjectiveFunction objective, double lowerBound, double upperBound) { return Minimum(objective, lowerBound, upperBound, XTolerance, MaximumIterations, MaximumExpansionSteps, LowerExpansionFactor, UpperExpansionFactor); } - public static MinimizationResult1D Minimum(IObjectiveFunction1D objective, double lowerBound, double upperBound, double xTolerance=1e-5, int maxIterations=1000, int maxExpansionSteps=10, double lowerExpansionFactor=2.0, double upperExpansionFactor=2.0) + public static ScalarMinimizationResult Minimum(IScalarObjectiveFunction objective, double lowerBound, double upperBound, double xTolerance=1e-5, int maxIterations=1000, int maxExpansionSteps=10, double lowerExpansionFactor=2.0, double upperExpansionFactor=2.0) { if (upperBound <= lowerBound) { @@ -61,9 +61,9 @@ namespace MathNet.Numerics.Optimization } double middlePointX = lowerBound + (upperBound - lowerBound)/(1 + Constants.GoldenRatio); - IEvaluation1D lower = objective.Evaluate(lowerBound); - IEvaluation1D middle = objective.Evaluate(middlePointX); - IEvaluation1D upper = objective.Evaluate(upperBound); + IScalarObjectiveFunctionEvaluation lower = objective.Evaluate(lowerBound); + IScalarObjectiveFunctionEvaluation middle = objective.Evaluate(middlePointX); + IScalarObjectiveFunctionEvaluation upper = objective.Evaluate(upperBound); ValueChecker(lower.Value, lowerBound); ValueChecker(middle.Value, middlePointX); @@ -135,7 +135,7 @@ namespace MathNet.Numerics.Optimization throw new MaximumIterationsException("Max iterations reached."); } - return new MinimizationResult1D(middle, iterations, ExitCondition.BoundTolerance); + return new ScalarMinimizationResult(middle, iterations, ExitCondition.BoundTolerance); } static void ValueChecker(double value, double point) diff --git a/src/Numerics/Optimization/IObjectiveFunction.cs b/src/Numerics/Optimization/IObjectiveFunction.cs index 8539f354..8530b290 100644 --- a/src/Numerics/Optimization/IObjectiveFunction.cs +++ b/src/Numerics/Optimization/IObjectiveFunction.cs @@ -59,4 +59,19 @@ namespace MathNet.Numerics.Optimization /// Create a new independent copy of this objective function, evaluated at the same point. IObjectiveFunction Fork(); } + + public interface IScalarObjectiveFunctionEvaluation + { + double Point { get; } + double Value { get; } + double Derivative { get; } + double SecondDerivative { get; } + } + + public interface IScalarObjectiveFunction + { + bool IsDerivativeSupported { get; } + bool IsSecondDerivativeSupported { get; } + IScalarObjectiveFunctionEvaluation Evaluate(double point); + } } diff --git a/src/Numerics/Optimization/ObjectiveFunction.cs b/src/Numerics/Optimization/ObjectiveFunction.cs index ce8551de..a0c981d9 100644 --- a/src/Numerics/Optimization/ObjectiveFunction.cs +++ b/src/Numerics/Optimization/ObjectiveFunction.cs @@ -90,5 +90,29 @@ namespace MathNet.Numerics.Optimization { return new LazyObjectiveFunction(function, gradient: gradient, hessian: hessian); } + + /// + /// Objective function where neither first nor second derivative is available. + /// + public static IScalarObjectiveFunction ScalarValue(Func function) + { + return new ScalarValueObjectiveFunction(function); + } + + /// + /// Objective function where the first derivative is available. + /// + public static IScalarObjectiveFunction ScalarDerivative(Func function, Func derivative) + { + return new ScalarObjectiveFunction(function, derivative); + } + + /// + /// Objective function where the first and second derivatives are available. + /// + public static IScalarObjectiveFunction ScalarSecondDerivative(Func function, Func derivative, Func secondDerivative) + { + return new ScalarObjectiveFunction(function, derivative, secondDerivative); + } } } diff --git a/src/Numerics/Optimization/ObjectiveFunction1D.cs b/src/Numerics/Optimization/ObjectiveFunctions/ScalarObjectiveFunction.cs similarity index 66% rename from src/Numerics/Optimization/ObjectiveFunction1D.cs rename to src/Numerics/Optimization/ObjectiveFunctions/ScalarObjectiveFunction.cs index 540f0feb..c11de220 100644 --- a/src/Numerics/Optimization/ObjectiveFunction1D.cs +++ b/src/Numerics/Optimization/ObjectiveFunctions/ScalarObjectiveFunction.cs @@ -29,47 +29,35 @@ using System; -namespace MathNet.Numerics.Optimization +namespace MathNet.Numerics.Optimization.ObjectiveFunctions { - public interface IEvaluation1D + internal class LazyScalarObjectiveFunctionEvaluation : IScalarObjectiveFunctionEvaluation { - 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; + double? _value; + double? _derivative; + double? _secondDerivative; + readonly ScalarObjectiveFunction _objectiveObject; + readonly double _point; - public CachedEvaluation1D(SimpleObjectiveFunction1D f, double point) + public LazyScalarObjectiveFunctionEvaluation(ScalarObjectiveFunction f, double point) { _objectiveObject = f; _point = point; } - private double SetValue() + + double SetValue() { _value = _objectiveObject.Objective(_point); return _value.Value; } - private double SetDerivative() + + double SetDerivative() { _derivative = _objectiveObject.Derivative(_point); return _derivative.Value; } - private double SetSecondDerivative() + + double SetSecondDerivative() { _secondDerivative = _objectiveObject.SecondDerivative(_point); return _secondDerivative.Value; @@ -79,49 +67,48 @@ namespace MathNet.Numerics.Optimization 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 + internal class ScalarObjectiveFunction : IScalarObjectiveFunction { public Func Objective { get; private set; } public Func Derivative { get; private set; } public Func SecondDerivative { get; private set; } - public SimpleObjectiveFunction1D(Func objective) + public ScalarObjectiveFunction(Func objective) { Objective = objective; Derivative = null; SecondDerivative = null; } - public SimpleObjectiveFunction1D(Func objective, Func derivative) + public ScalarObjectiveFunction(Func objective, Func derivative) { Objective = objective; Derivative = derivative; SecondDerivative = null; } - public SimpleObjectiveFunction1D(Func objective, Func derivative, Func secondDerivative) + public ScalarObjectiveFunction(Func objective, Func derivative, Func secondDerivative) { Objective = objective; Derivative = derivative; SecondDerivative = secondDerivative; } - public bool DerivativeSupported + public bool IsDerivativeSupported { get { return Derivative != null; } } - public bool SecondDerivativeSupported + public bool IsSecondDerivativeSupported { get { return SecondDerivative != null; } } - public IEvaluation1D Evaluate(double point) + public IScalarObjectiveFunctionEvaluation Evaluate(double point) { - return new CachedEvaluation1D(this, point); + return new LazyScalarObjectiveFunctionEvaluation(this, point); } } } diff --git a/src/Numerics/Optimization/ObjectiveFunctions/ScalarValueObjectiveFunction.cs b/src/Numerics/Optimization/ObjectiveFunctions/ScalarValueObjectiveFunction.cs new file mode 100644 index 00000000..81ef07c9 --- /dev/null +++ b/src/Numerics/Optimization/ObjectiveFunctions/ScalarValueObjectiveFunction.cs @@ -0,0 +1,80 @@ +// +// Math.NET Numerics, part of the Math.NET Project +// http://numerics.mathdotnet.com +// http://github.com/mathnet/mathnet-numerics +// +// Copyright (c) 2009-2017 Math.NET +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +using System; + +namespace MathNet.Numerics.Optimization.ObjectiveFunctions +{ + internal class ScalarValueObjectiveFunctionEvaluation : IScalarObjectiveFunctionEvaluation + { + public ScalarValueObjectiveFunctionEvaluation(double point, double value) + { + Point = point; + Value = value; + } + + public double Point { get; } + public double Value { get; } + + public double Derivative + { + get { throw new NotSupportedException(); } + } + + public double SecondDerivative + { + get { throw new NotSupportedException(); } + } + } + + internal class ScalarValueObjectiveFunction : IScalarObjectiveFunction + { + public Func Objective { get; private set; } + + public ScalarValueObjectiveFunction(Func objective) + { + Objective = objective; + } + + public bool IsDerivativeSupported + { + get { return false; } + } + + public bool IsSecondDerivativeSupported + { + get { return false; } + } + + public IScalarObjectiveFunctionEvaluation Evaluate(double point) + { + return new ScalarValueObjectiveFunctionEvaluation(point, Objective(point)); + } + } +} diff --git a/src/Numerics/Optimization/MinimizationResult1D.cs b/src/Numerics/Optimization/ScalarMinimizationResult.cs similarity index 86% rename from src/Numerics/Optimization/MinimizationResult1D.cs rename to src/Numerics/Optimization/ScalarMinimizationResult.cs index 6319fdc2..8fd69e19 100644 --- a/src/Numerics/Optimization/MinimizationResult1D.cs +++ b/src/Numerics/Optimization/ScalarMinimizationResult.cs @@ -29,14 +29,14 @@ namespace MathNet.Numerics.Optimization { - public class MinimizationResult1D + public class ScalarMinimizationResult { public double MinimizingPoint { get { return FunctionInfoAtMinimum.Point; } } - public IEvaluation1D FunctionInfoAtMinimum { get; private set; } + public IScalarObjectiveFunctionEvaluation FunctionInfoAtMinimum { get; private set; } public int Iterations { get; private set; } public ExitCondition ReasonForExit { get; private set; } - public MinimizationResult1D(IEvaluation1D functionInfo, int iterations, ExitCondition reasonForExit) + public ScalarMinimizationResult(IScalarObjectiveFunctionEvaluation functionInfo, int iterations, ExitCondition reasonForExit) { FunctionInfoAtMinimum = functionInfo; Iterations = iterations; diff --git a/src/UnitTests/OptimizationTests/GoldenSectionMinimizerTests.cs b/src/UnitTests/OptimizationTests/GoldenSectionMinimizerTests.cs index 6ff308ae..c4329beb 100644 --- a/src/UnitTests/OptimizationTests/GoldenSectionMinimizerTests.cs +++ b/src/UnitTests/OptimizationTests/GoldenSectionMinimizerTests.cs @@ -41,7 +41,7 @@ namespace MathNet.Numerics.UnitTests.OptimizationTests { var algorithm = new GoldenSectionMinimizer(1e-5, 1000); var f1 = new Func(x => (x - 3)*(x - 3)); - var obj = new SimpleObjectiveFunction1D(f1); + var obj = ObjectiveFunction.ScalarValue(f1); var r1 = GoldenSectionMinimizer.Minimum(obj, -100, 100); Assert.That(Math.Abs(r1.MinimizingPoint - 3.0), Is.LessThan(1e-4)); @@ -52,7 +52,7 @@ namespace MathNet.Numerics.UnitTests.OptimizationTests { var algorithm = new GoldenSectionMinimizer(1e-5, 1000); var f1 = new Func(x => (x - 3)*(x - 3)); - var obj = new SimpleObjectiveFunction1D(f1); + var obj = ObjectiveFunction.ScalarValue(f1); var r1 = algorithm.FindMinimum(obj, -5, 5); Assert.That(Math.Abs(r1.MinimizingPoint - 3.0), Is.LessThan(1e-4));