// // 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; using IStation.Numerics.LinearAlgebra; namespace IStation.Numerics.Optimization.ObjectiveFunctions { /// /// 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. /// public class ForwardDifferenceGradientObjectiveFunction : IObjectiveFunction { public IObjectiveFunction InnerObjectiveFunction { get; protected set; } protected Vector LowerBound { get; set; } protected Vector UpperBound { get; set; } protected bool ValueEvaluated { get; set; } = false; protected bool GradientEvaluated { get; set; } = false; private Vector _gradient; public double MinimumIncrement { get; set; } public double RelativeIncrement { get; set; } public ForwardDifferenceGradientObjectiveFunction(IObjectiveFunction valueOnlyObj, Vector lowerBound, Vector 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 tmpPoint = Point.Clone(); var tmpObj = InnerObjectiveFunction.CreateNew(); for (int ii = 0; ii < _gradient.Count; ++ii) { var origPoint = tmpPoint[ii]; var relIncr = origPoint * RelativeIncrement; var h = Math.Max(relIncr, MinimumIncrement); var mult = 1; if (origPoint + h > UpperBound[ii]) mult = -1; tmpPoint[ii] = origPoint + mult*h; tmpObj.EvaluateAt(tmpPoint); double bumpedValue = tmpObj.Value; _gradient[ii] = (mult * bumpedValue - mult * InnerObjectiveFunction.Value) / h; tmpPoint[ii] = origPoint; } GradientEvaluated = true; } public Vector Gradient { get { if (!GradientEvaluated) EvaluateGradient(); return _gradient; } protected set => _gradient = value; } public Matrix Hessian => throw new NotImplementedException(); public bool IsGradientSupported => true; public bool IsHessianSupported => false; public Vector Point { get; protected set; } public double Value { get { if (!ValueEvaluated) EvaluateValue(); return this.InnerObjectiveFunction.Value; } } public IObjectiveFunction CreateNew() { var tmp = new ForwardDifferenceGradientObjectiveFunction(InnerObjectiveFunction.CreateNew(), LowerBound, UpperBound, this.RelativeIncrement, this.MinimumIncrement); return tmp; } public void EvaluateAt(Vector point) { Point = point; ValueEvaluated = false; GradientEvaluated = false; InnerObjectiveFunction.EvaluateAt(point); } public IObjectiveFunction Fork() { return new ForwardDifferenceGradientObjectiveFunction(InnerObjectiveFunction.Fork(), LowerBound, UpperBound, this.RelativeIncrement, this.MinimumIncrement) { Point = Point?.Clone(), GradientEvaluated = GradientEvaluated, ValueEvaluated = ValueEvaluated, _gradient = _gradient?.Clone() }; } } }