/// <summary> /// Returns the gradient of the specified parametric function /// at the given argument. /// </summary> /// <param name="function"> /// The function to be differentiated. /// </param> /// <param name="argument"> /// The argument at which the gradient must be evaluated. /// </param> /// <param name="parameter"> /// The function parameter. /// </param> /// <typeparam name="TFunctionParameter"> /// The type of the function parameter. /// </typeparam> /// <returns> /// The gradient of the specified function evaluated /// at the given argument and parameter. /// </returns> /// <exception cref="ArgumentNullException"> /// <paramref name="function"/> is <b>null</b>.<br/> /// -or-<br/> /// <paramref name="argument"/> is <b>null</b>. /// </exception> public static DoubleMatrix Gradient <TFunctionParameter>( Func <DoubleMatrix, TFunctionParameter, double> function, DoubleMatrix argument, TFunctionParameter parameter) { if (function is null) { throw new ArgumentNullException(nameof(function)); } if (argument is null) { throw new ArgumentNullException(nameof(argument)); } int numberOfArguments = argument.Count; DoubleMatrix gradient = DoubleMatrix.Dense( numberOfArguments, 1); double h, h_by_2, arg_i, partialDerivative; DoubleMatrix x = (DoubleMatrix)argument.Clone(); // Using central differences for an accuracy of order 4 // See also http://en.wikipedia.org/wiki/Finite_difference_coefficients // // Points: -2 -1 0 1 2 // Coefficients: 1/12 −2/3 0 2/3 −1/12 for (int i = 0; i < numberOfArguments; i++) { arg_i = argument[i]; h = NumericalDifferentiation.GetFirstOrderDelta(arg_i); h_by_2 = h * 2.0; partialDerivative = 0.0; // argument - 2*h x[i] -= h_by_2; partialDerivative += function(x, parameter); x[i] = arg_i; // argument - h x[i] -= h; partialDerivative -= 8.0 * function(x, parameter); x[i] = arg_i; // argument + h x[i] += h; partialDerivative += 8.0 * function(x, parameter); x[i] = arg_i; // argument + 2*h x[i] += h_by_2; partialDerivative -= function(x, parameter); x[i] = arg_i; partialDerivative /= (12.0 * h); gradient[i] = partialDerivative; } return(gradient); }
/// <summary> /// Returns the Hessian matrix of the specified parametric function /// at the given argument. /// </summary> /// <param name="function"> /// The function to be differentiated. /// </param> /// <param name="argument"> /// The argument at which the Hessian must be evaluated. /// </param> /// <param name="parameter"> /// The function parameter. /// </param> /// <typeparam name="TFunctionParameter"> /// The type of the function parameter. /// </typeparam> /// <returns> /// The Hessian matrix of the specified function evaluated /// at the given argument. /// </returns> /// <exception cref="ArgumentNullException"> /// <paramref name="function"/> is <b>null</b>.<br/> /// -or-<br/> /// <paramref name="argument"/> is <b>null</b>. /// </exception> public static DoubleMatrix Hessian <TFunctionParameter>( Func <DoubleMatrix, TFunctionParameter, double> function, DoubleMatrix argument, TFunctionParameter parameter) { if (function is null) { throw new ArgumentNullException(nameof(function)); } if (argument is null) { throw new ArgumentNullException(nameof(argument)); } int numberOfArguments = argument.Count; DoubleMatrix hessian = DoubleMatrix.Dense( numberOfArguments, numberOfArguments); double h, arg_i; DoubleMatrix x = (DoubleMatrix)argument.Clone(); double secondPartialDerivative; double h2; for (int i = 0; i < numberOfArguments; i++) { arg_i = argument[i]; h = NumericalDifferentiation.GetSecondOrderDelta(arg_i); h2 = h * h; secondPartialDerivative = 0.0; // argument - h x[i] -= h; secondPartialDerivative += function(x, parameter); x[i] = arg_i; // argument secondPartialDerivative -= 2.0 * function(x, parameter); // argument + h x[i] += h; secondPartialDerivative += function(x, parameter); x[i] = arg_i; secondPartialDerivative /= h2; hessian[i, i] = secondPartialDerivative; } // Mixed partial derivatives // Points: -1,-1 -1,1 0 1,-1 1,1 // Coefficients: 1/4 -1/4 0 -1/4 1/4 double mixedPartialDerivative, k, arg_j, denominator; for (int i = 0; i < numberOfArguments; i++) { arg_i = argument[i]; h = NumericalDifferentiation.GetSecondOrderDelta(arg_i); for (int j = i + 1; j < numberOfArguments; j++) { arg_j = argument[j]; k = NumericalDifferentiation.GetSecondOrderDelta(arg_j); denominator = h * k; mixedPartialDerivative = 0.0; // -1, -1 x[i] -= h; x[j] -= k; mixedPartialDerivative += .25 * function(x, parameter); x[i] = arg_i; x[j] = arg_j; // Points: -1,-1 -1,1 0 1,-1 1,1 // Coefficients: 1/4 -1/4 0 -1/4 1/4 // -1, 1 x[i] -= h; x[j] += k; mixedPartialDerivative -= .25 * function(x, parameter); x[i] = arg_i; x[j] = arg_j; // 1,-1 x[i] += h; x[j] -= k; mixedPartialDerivative -= .25 * function(x, parameter); x[i] = arg_i; x[j] = arg_j; // 1, 1 x[i] += h; x[j] += k; mixedPartialDerivative += .25 * function(x, parameter); x[i] = arg_i; x[j] = arg_j; mixedPartialDerivative /= denominator; hessian[i, j] = mixedPartialDerivative; hessian[j, i] = mixedPartialDerivative; } } return(hessian); }