public void GradientDescentTest() { Func <double, double> func = Math.Sin; const double lower = 0; const double upper = 2 * Math.PI; const double rate = 0.5; double result1 = GradientDescentUtil.GradientDescent(func, lower, upper, rate); Assert.IsTrue(Math.Abs(result1 - 1.5 * Math.PI) < 0.001); double result2 = GradientDescentUtil.GradientAscent(func, lower, upper, rate); Assert.IsTrue(Math.Abs(result2 - 0.5 * Math.PI) < 0.001); double result3 = GradientDescentUtil.GradientDescent(func, lower + 4 * Math.PI, upper + 4 * Math.PI, rate); Assert.IsTrue(Math.Abs(result3 - (1.5 * Math.PI + 4 * Math.PI)) < 0.001); }
public static double GetMinIntegral(IReadOnlyList <IGraphAnchor> anchors) { double height = 0; double minValue = double.PositiveInfinity; IGraphAnchor previousAnchor = null; foreach (var anchor in anchors) { if (previousAnchor != null) { var p1 = previousAnchor.Pos; var p2 = anchor.Pos; var difference = p2 - p1; if (difference.X < Precision.DOUBLE_EPSILON) { previousAnchor = anchor; continue; } // Update the interpolator with the tension of the anchor. This doesn't happen automatically anchor.Interpolator.P = anchor.Tension; double minIntegral; double endIntegral; if (anchor.Interpolator is IIntegrableInterpolator integrableInterpolator) { endIntegral = integrableInterpolator.GetIntegral(0, 1) * difference.X * difference.Y + difference.X * p1.Y; // If the interpolator has a CustomIntegralExtremaAttribute than we check the min/max integral for all the specified locations var customExtremaAttribute = anchor.Interpolator.GetType().GetCustomAttribute <CustomIntegralExtremaAttribute>(); if (customExtremaAttribute != null) { minIntegral = customExtremaAttribute.ExtremaPositions .Select(o => integrableInterpolator.GetIntegral(0, o) * difference.X * difference.Y + o * difference.X * p1.Y).Min(); } else { minIntegral = integrableInterpolator.GetIntegral(0, 1) * difference.X * difference.Y + difference.X * p1.Y; } // Check if the interpolation passes through 0 if (difference.Y * p1.Y < 0) { // Possibility of max/min not at endpoints. Need to calculate the hard way double newMinIntegral; if (integrableInterpolator is IInvertibleInterpolator invertibleInterpolator) { // Calculate all the zeros of the interpolation var zeros = invertibleInterpolator.GetInverse(-p1.Y / difference.Y).Where(o => o <= 1 && o >= 0).ToArray(); if (zeros.Length > 0) { newMinIntegral = zeros.Select(z => integrableInterpolator.GetIntegral(0, z) * difference.X * difference.Y + z * difference.X * p1.Y).Min(); } else { newMinIntegral = double.PositiveInfinity; } } else { double newMinIntegralPosition = GradientDescentUtil.GradientDescent( d => integrableInterpolator.GetIntegral(0, d) * difference.X * difference.Y + d * difference.X * p1.Y, 0, 1, 0.1); newMinIntegral = integrableInterpolator.GetIntegral(0, newMinIntegralPosition) * difference.X * difference.Y + newMinIntegralPosition * difference.X * p1.Y; } if (newMinIntegral < minIntegral) { minIntegral = newMinIntegral; } } } else { endIntegral = 0.5 * difference.X * difference.Y + difference.X * p1.Y; minIntegral = endIntegral; // Check if the interpolation passes through 0 if (difference.Y * p1.Y < 0) { // Possibility of max/min not at endpoints. For a linear interpolator this is possible to solve algebraically var x = -p1.Y / difference.Y; if (x >= 0 && x <= 1) { double newMinIntegral = 0.5 * Math.Pow(x, 2) * difference.X * difference.Y + x * difference.X * p1.Y; if (newMinIntegral > minIntegral) { minIntegral = newMinIntegral; } } } } var possibleMinValue = height + minIntegral; if (possibleMinValue < minValue) { minValue = possibleMinValue; } height += endIntegral; } previousAnchor = anchor; } return(minValue); }