// // Math.NET Numerics, part of the Math.NET Project // http://numerics.mathdotnet.com // http://github.com/mathnet/mathnet-numerics // // 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. // using System; using System.Linq; namespace IStation.Numerics.Differentiation { /// /// Type of finite different step size. /// public enum StepType { /// /// The absolute step size value will be used in numerical derivatives, regardless of order or function parameters. /// Absolute, /// /// A base step size value, h, will be scaled according to the function input parameter. A common example is hx = h*(1+abs(x)), however /// this may vary depending on implementation. This definition only guarantees that the only scaling will be relative to the /// function input parameter and not the order of the finite difference derivative. /// RelativeX, /// /// A base step size value, eps (typically machine precision), is scaled according to the finite difference coefficient order /// and function input parameter. The initial scaling according to finite different coefficient order can be thought of as producing a /// base step size, h, that is equivalent to scaling. This step size is then scaled according to the function /// input parameter. Although implementation may vary, an example of second order accurate scaling may be (eps)^(1/3)*(1+abs(x)). /// Relative }; /// /// Class to evaluate the numerical derivative of a function using finite difference approximations. /// Variable point and center methods can be initialized . /// This class can also be used to return function handles (delegates) for a fixed derivative order and variable. /// It is possible to evaluate the derivative and partial derivative of univariate and multivariate functions respectively. /// public class NumericalDerivative { readonly int _points; int _center; double _stepSize = Math.Pow(2, -10); double _epsilon = Precision.PositiveMachineEpsilon; double _baseStepSize = Math.Pow(2, -26); readonly FiniteDifferenceCoefficients _coefficients; /// /// Initializes a NumericalDerivative class with the default 3 point center difference method. /// public NumericalDerivative() : this(3, 1) { } /// /// Initialized a NumericalDerivative class. /// /// Number of points for finite difference derivatives. /// Location of the center with respect to other points. Value ranges from zero to points-1. public NumericalDerivative(int points, int center) { if (points < 2) { throw new ArgumentOutOfRangeException(nameof(points), "Points must be two or greater."); } _center = center; _points = points; Center = center; _coefficients = new FiniteDifferenceCoefficients(points); } /// /// Sets and gets the finite difference step size. This value is for each function evaluation if relative step size types are used. /// If the base step size used in scaling is desired, see . /// /// /// Setting then getting the StepSize may return a different value. This is not unusual since a user-defined step size is converted to a /// base-2 representable number to improve finite difference accuracy. /// public double StepSize { get => _stepSize; set { //Base 2 yields more accurate results... var p = Math.Log(Math.Abs(value))/Math.Log(2); _stepSize = Math.Pow(2, Math.Round(p)); } } /// /// Sets and gets the base finite difference step size. This assigned value to this parameter is only used if is set to RelativeX. /// However, if the StepType is Relative, it will contain the base step size computed from based on the finite difference order. /// public double BaseStepSize { get => _baseStepSize; set { //Base 2 yields more accurate results... var p = Math.Log(Math.Abs(value)) / Math.Log(2); _baseStepSize = Math.Pow(2, Math.Round(p)); } } /// /// Sets and gets the base finite difference step size. This parameter is only used if is set to Relative. /// By default this is set to machine epsilon, from which is computed. /// public double Epsilon { get => _epsilon; set { //Base 2 yields more accurate results... var p = Math.Log(Math.Abs(value)) / Math.Log(2); _epsilon = Math.Pow(2, Math.Round(p)); } } /// /// Sets and gets the location of the center point for the finite difference derivative. /// public int Center { get => _center; set { if (value >= _points || value < 0) throw new ArgumentOutOfRangeException(nameof(value), "Center must lie between 0 and points -1"); _center = value; } } /// /// Number of times a function is evaluated for numerical derivatives. /// public int Evaluations { get; private set; } /// /// Type of step size for computing finite differences. If set to absolute, dx = h. /// If set to relative, dx = (1+abs(x))*h^(2/(order+1)). This provides accurate results when /// h is approximately equal to the square-root of machine accuracy, epsilon. /// public StepType StepType { get; set; } = StepType.Relative; /// /// Evaluates the derivative of equidistant points using the finite difference method. /// /// Vector of points StepSize apart. /// Derivative order. /// Finite difference step size. /// Derivative of points of the specified order. public double EvaluateDerivative(double[] points, int order, double stepSize) { if (points == null) throw new ArgumentNullException(nameof(points)); if (order >= _points || order < 0) throw new ArgumentOutOfRangeException(nameof(order), "Order must be between zero and points-1."); var c = _coefficients.GetCoefficients(Center, order); var result = c.Select((t, i) => t*points[i]).Sum(); result /= Math.Pow(stepSize, order); return result; } /// /// Evaluates the derivative of a scalar univariate function. /// /// /// Supplying the optional argument currentValue will reduce the number of function evaluations /// required to calculate the finite difference derivative. /// /// Function handle. /// Point at which to compute the derivative. /// Derivative order. /// Current function value at center. /// Function derivative at x of the specified order. public double EvaluateDerivative(Func f, double x, int order, double? currentValue = null) { var c = _coefficients.GetCoefficients(Center, order); var h = CalculateStepSize(_points, x, order); var points = new double[_points]; for (int i = 0; i < _points; i++) { if (i == Center && currentValue.HasValue) points[i] = currentValue.Value; else if(c[i] != 0) // Only evaluate function if it will actually be used. { points[i] = f(x + (i - Center) * h); Evaluations++; } } return EvaluateDerivative(points, order, h); } /// /// Creates a function handle for the derivative of a scalar univariate function. /// /// Input function handle. /// Derivative order. /// Function handle that evaluates the derivative of input function at a fixed order. public Func CreateDerivativeFunctionHandle(Func f, int order) { return x => EvaluateDerivative(f, x, order); } /// /// Evaluates the partial derivative of a multivariate function. /// /// Multivariate function handle. /// Vector at which to evaluate the derivative. /// Index of independent variable for partial derivative. /// Derivative order. /// Current function value at center. /// Function partial derivative at x of the specified order. public double EvaluatePartialDerivative(Func f, double[] x, int parameterIndex, int order, double? currentValue = null) { var xi = x[parameterIndex]; var c = _coefficients.GetCoefficients(Center, order); var h = CalculateStepSize(_points, x[parameterIndex], order); var points = new double[_points]; for (int i = 0; i < _points; i++) { if (i == Center && currentValue.HasValue) points[i] = currentValue.Value; else if(c[i] != 0) // Only evaluate function if it will actually be used. { x[parameterIndex] = xi + (i - Center) * h; points[i] = f(x); Evaluations++; } } //restore original value x[parameterIndex] = xi; return EvaluateDerivative(points, order, h); } /// /// Evaluates the partial derivatives of a multivariate function array. /// /// /// This function assumes the input vector x is of the correct length for f. /// /// Multivariate vector function array handle. /// Vector at which to evaluate the derivatives. /// Index of independent variable for partial derivative. /// Derivative order. /// Current function value at center. /// Vector of functions partial derivatives at x of the specified order. public double[] EvaluatePartialDerivative(Func[] f, double[] x, int parameterIndex, int order, double?[] currentValue = null) { var df = new double[f.Length]; for (int i = 0; i < f.Length; i++) { if(currentValue != null && currentValue[i].HasValue) df[i] = EvaluatePartialDerivative(f[i], x, parameterIndex, order, currentValue[i]!.Value); else df[i] = EvaluatePartialDerivative(f[i], x, parameterIndex, order); } return df; } /// /// Creates a function handle for the partial derivative of a multivariate function. /// /// Input function handle. /// Index of the independent variable for partial derivative. /// Derivative order. /// Function handle that evaluates partial derivative of input function at a fixed order. public Func CreatePartialDerivativeFunctionHandle(Func f, int parameterIndex, int order) { return x => EvaluatePartialDerivative(f, x, parameterIndex, order); } /// /// Creates a function handle for the partial derivative of a vector multivariate function. /// /// Input function handle. /// Index of the independent variable for partial derivative. /// Derivative order. /// Function handle that evaluates partial derivative of input function at fixed order. public Func CreatePartialDerivativeFunctionHandle(Func[] f, int parameterIndex, int order) { return x => EvaluatePartialDerivative(f, x, parameterIndex, order); } /// /// Evaluates the mixed partial derivative of variable order for multivariate functions. /// /// /// This function recursively uses to evaluate mixed partial derivative. /// Therefore, it is more efficient to call for higher order derivatives of /// a single independent variable. /// /// Multivariate function handle. /// Points at which to evaluate the derivative. /// Vector of indices for the independent variables at descending derivative orders. /// Highest order of differentiation. /// Current function value at center. /// Function mixed partial derivative at x of the specified order. public double EvaluateMixedPartialDerivative(Func f, double[] x, int[] parameterIndex, int order, double? currentValue = null) { if (parameterIndex.Length != order) throw new ArgumentOutOfRangeException(nameof(parameterIndex), "The number of parameters must match derivative order."); if (order == 1) return EvaluatePartialDerivative(f, x, parameterIndex[0], order, currentValue); int reducedOrder = order - 1; var reducedParameterIndex = new int[reducedOrder]; Array.Copy(parameterIndex, 0, reducedParameterIndex, 0, reducedOrder); var points = new double[_points]; var currentParameterIndex = parameterIndex[order - 1]; var h = CalculateStepSize(_points, x[currentParameterIndex], order); var xi = x[currentParameterIndex]; for (int i = 0; i < _points; i++) { x[currentParameterIndex] = xi + (i - Center)*h; points[i] = EvaluateMixedPartialDerivative(f, x, reducedParameterIndex, reducedOrder); } // restore original value x[currentParameterIndex] = xi; // This will always be to the first order return EvaluateDerivative(points, 1, h); } /// /// Evaluates the mixed partial derivative of variable order for multivariate function arrays. /// /// /// This function recursively uses to evaluate mixed partial derivative. /// Therefore, it is more efficient to call for higher order derivatives of /// a single independent variable. /// /// Multivariate function array handle. /// Vector at which to evaluate the derivative. /// Vector of indices for the independent variables at descending derivative orders. /// Highest order of differentiation. /// Current function value at center. /// Function mixed partial derivatives at x of the specified order. public double[] EvaluateMixedPartialDerivative(Func[] f, double[] x, int[] parameterIndex, int order, double?[]? currentValue = null) { var df = new double[f.Length]; for (int i = 0; i < f.Length; i++) { if(currentValue != null && currentValue[i].HasValue) df[i] = EvaluateMixedPartialDerivative(f[i], x, parameterIndex, order, currentValue[i]!.Value); else df[i] = EvaluateMixedPartialDerivative(f[i], x, parameterIndex, order); } return df; } /// /// Creates a function handle for the mixed partial derivative of a multivariate function. /// /// Input function handle. /// Vector of indices for the independent variables at descending derivative orders. /// Highest derivative order. /// Function handle that evaluates the fixed mixed partial derivative of input function at fixed order. public Func CreateMixedPartialDerivativeFunctionHandle(Func f, int[] parameterIndex, int order) { return x => EvaluateMixedPartialDerivative(f, x, parameterIndex, order); } /// /// Creates a function handle for the mixed partial derivative of a multivariate vector function. /// /// Input vector function handle. /// Vector of indices for the independent variables at descending derivative orders. /// Highest derivative order. /// Function handle that evaluates the fixed mixed partial derivative of input function at fixed order. public Func CreateMixedPartialDerivativeFunctionHandle(Func[] f, int[] parameterIndex, int order) { return x => EvaluateMixedPartialDerivative(f, x, parameterIndex, order); } /// /// Resets the evaluation counter. /// public void ResetEvaluations() { Evaluations = 0; } private double[] CalculateStepSize(int points, double[] x, double order) { var h = new double[x.Length]; for (int i = 1; i < h.Length; i++) h[i] = CalculateStepSize(points, x[i], order); return h; } private double CalculateStepSize(int points, double x, double order) { // Step size relative to function input parameter if (StepType == StepType.RelativeX) { StepSize = BaseStepSize*(1 + Math.Abs(x)); } // Step size relative to function input parameter and order else if (StepType == StepType.Relative) { var accuracy = points - order; BaseStepSize = Math.Pow(Epsilon,(1/(accuracy + order))); StepSize = BaseStepSize*(1 + Math.Abs(x)); } // Do nothing for absolute step size. return StepSize; } } }