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);
        }
Пример #2
0
        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);
        }