public void FindExtremaNegativeGamma()
        {
            // https://en.wikipedia.org/wiki/Particular_values_of_the_Gamma_function#Other_constants

            Tuple <double, double>[] extrema = new Tuple <double, double>[] {
                Tuple.Create(-0.5040830082644554092582693045, -3.5446436111550050891219639933),
                Tuple.Create(-1.5734984731623904587782860437, 2.3024072583396801358235820396),
                Tuple.Create(-2.6107208684441446500015377157, -0.8881363584012419200955280294),
                Tuple.Create(-3.6352933664369010978391815669, 0.2451275398343662504382300889)
            };

            foreach (Tuple <double, double> extremum in extrema)
            {
                // We use brackets since we know the extremum must lie between the singularities.
                // We should be able to use the actual singularities at endpoints, but this doesn't work; look into it.

                Interval bracket = Interval.FromEndpoints(Math.Floor(extremum.Item1) + 0.01, Math.Ceiling(extremum.Item1) - 0.01);
                Extremum result;
                if (extremum.Item2 < 0.0)
                {
                    result = FunctionMath.FindMaximum(AdvancedMath.Gamma, bracket);
                }
                else
                {
                    result = FunctionMath.FindMinimum(AdvancedMath.Gamma, bracket);
                }
                Assert.IsTrue(result.Bracket.OpenContains(extremum.Item1));
                Assert.IsTrue(result.Bracket.OpenContains(result.Location));
                Assert.IsTrue(TestUtilities.IsNearlyEqual(result.Value, extremum.Item2));
            }
        }
 public void FindMinimaFromPoint()
 {
     foreach (TestExtremum testExtremum in testMinima)
     {
         Extremum extremum = FunctionMath.FindMinimum(testExtremum.Function, testExtremum.Interval.Midpoint);
         Assert.IsTrue(TestUtilities.IsNearlyEqual(extremum.Location, testExtremum.Location, Math.Sqrt(TestUtilities.TargetPrecision)));
         Assert.IsTrue(TestUtilities.IsNearlyEqual(extremum.Value, testExtremum.Value, TestUtilities.TargetPrecision));
         //if (!Double.IsNaN(testMinimum.Curvature)) Assert.IsTrue(TestUtilities.IsNearlyEqual(minimum.Curvature, testMinimum.Curvature, 0.05));
     }
 }
        public void FindMinimumOfGamma()
        {
            int count = 0;
            ExtremumSettings settings = new ExtremumSettings()
            {
                Listener = r => { count++; }
            };

            Func <double, double> f = new Func <double, double>(AdvancedMath.Gamma);
            Extremum minimum        = FunctionMath.FindMinimum(f, 1.5, settings);

            Assert.IsTrue(TestUtilities.IsNearlyEqual(minimum.Location, 1.46163214496836234126, Math.Sqrt(TestUtilities.TargetPrecision)));
            Assert.IsTrue(TestUtilities.IsNearlyEqual(minimum.Value, 0.88560319441088870028));
            //Assert.IsTrue(TestUtilities.IsNearlyEqual(minimum.Curvature, Math.Sqrt(Math.Sqrt(0.8569736317111708))));
            Assert.IsTrue(count > 0);
        }
        /// <summary>
        /// Fits the data to an arbitrary parameterized function.
        /// </summary>
        /// <param name="function">The fit function.</param>
        /// <param name="start">An initial guess at the parameters.</param>
        /// <returns>A fit result containing the best-fitting function parameters
        /// and a &#x3C7;<sup>2</sup> test of the quality of the fit.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="function"/> or <paramref name="start"/> are <see langword="null"/>.</exception>
        /// <exception cref="InsufficientDataException">There are fewer data points than fit parameters.</exception>
        /// <exception cref="DivideByZeroException">The curvature matrix is singular, indicating that the data is independent of
        /// one or more parameters, or that two or more parameters are linearly dependent.</exception>
        public FitResult FitToFunction(Func <double[], T, double> function, double[] start)
        {
            if (function == null)
            {
                throw new ArgumentNullException(nameof(function));
            }
            if (start == null)
            {
                throw new ArgumentNullException(nameof(start));
            }

            // you can't do a fit with less data than parameters
            if (this.Count < start.Length)
            {
                throw new InsufficientDataException();
            }

            /*
             * Func<IList<double>, double> function0 = (IList<double> x0) => {
             *  double[] x = new double[x0.Count];
             *  x0.CopyTo(x, 0);
             *  return(function(x));
             * };
             * MultiExtremum minimum0 = MultiFunctionMath.FindMinimum(function0, start);
             */

            // create a chi^2 fit metric and minimize it
            FitMetric <T> metric  = new FitMetric <T>(this, function);
            SpaceExtremum minimum = FunctionMath.FindMinimum(new Func <double[], double>(metric.Evaluate), start);

            // compute the covariance (Hessian) matrix by inverting the curvature matrix
            SymmetricMatrix       A  = 0.5 * minimum.Curvature();
            CholeskyDecomposition CD = A.CholeskyDecomposition(); // should not return null if we were at a minimum

            if (CD == null)
            {
                throw new DivideByZeroException();
            }
            SymmetricMatrix C = CD.Inverse();

            // package up the results and return them
            TestResult test = new TestResult("ChiSquare", minimum.Value, TestType.RightTailed, new ChiSquaredDistribution(this.Count - minimum.Dimension));
            FitResult  fit  = new FitResult(minimum.Location(), C, test);

            return(fit);
        }
        /// <summary>
        /// Fits the data to an arbitrary parameterized function.
        /// </summary>
        /// <param name="function">The fit function.</param>
        /// <param name="start">An initial guess at the parameters.</param>
        /// <returns>A fit result containing the best-fitting function parameters
        /// and a &#x3C7;<sup>2</sup> test of the quality of the fit.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="function"/> or <paramref name="start"/> are <see langword="null"/>.</exception>
        /// <exception cref="InsufficientDataException">There are fewer data points than fit parameters.</exception>
        /// <exception cref="DivideByZeroException">The curvature matrix is singular, indicating that the data is independent of
        /// one or more parameters, or that two or more parameters are linearly dependent.</exception>
        public UncertainMeasurementFitResult FitToFunction(Func <double[], T, double> function, double[] start)
        {
            if (function == null)
            {
                throw new ArgumentNullException(nameof(function));
            }
            if (start == null)
            {
                throw new ArgumentNullException(nameof(start));
            }

            // you can't do a fit with less data than parameters
            if (this.Count < start.Length)
            {
                throw new InsufficientDataException();
            }

            // create a chi^2 fit metric and minimize it
            FitMetric <T> metric  = new FitMetric <T>(this, function);
            SpaceExtremum minimum = FunctionMath.FindMinimum(new Func <double[], double>(metric.Evaluate), start);

            // compute the covariance (Hessian) matrix by inverting the curvature matrix
            SymmetricMatrix       A  = 0.5 * minimum.Curvature();
            CholeskyDecomposition CD = A.CholeskyDecomposition(); // should not return null if we were at a minimum

            if (CD == null)
            {
                throw new DivideByZeroException();
            }
            SymmetricMatrix C = CD.Inverse();

            // package up the results and return them
            TestResult          test       = new TestResult("χ²", minimum.Value, new ChiSquaredDistribution(this.Count - minimum.Dimension), TestType.RightTailed);
            ParameterCollection parameters = new ParameterCollection(NumberNames(start.Length), new ColumnVector(minimum.Location(), 0, 1, start.Length, true), C);

            return(new UncertainMeasurementFitResult(parameters, test));
        }
Exemple #6
0
        /// <summary>
        /// Fits an MA(1) model to the time series.
        /// </summary>
        /// <param name="series">The data series.</param>
        /// <returns>The fit, with parameters lag-1 coefficient, mean, and standard deviation.</returns>
        public static MA1FitResult FitToMA1(this IReadOnlyList <double> series)
        {
            if (series == null)
            {
                throw new ArgumentNullException(nameof(series));
            }
            if (series.Count < 4)
            {
                throw new InsufficientDataException();
            }

            // MA(1) model is
            //   y_t - \mu = u_t + \beta u_{t-1}
            // where u_t ~ N(0, \sigma) are IID.

            // It's easy to show that
            //   m = E(y_t) = \mu
            //   c_0 = V(y_t) = E((y_t - m)^2) = (1 + \beta^2) \sigma^2
            //   c_1 = V(y_t, y_{t-1}) = \beta \sigma^2
            // So the method of moments gives
            //   \beta = \frac{1 - \sqrt{1 - (2 g_1)^2}}{2}
            //   \mu = m
            //   \sigma^2 = \frac{c_0}{1 + \beta^2}
            // It turns out these are very poor (high bias, high variance) estimators,
            // but they do illustrate the basic requirement that g_1 < 1/2.

            // The MLE estimator

            int n = series.Count;

            double m = series.Mean();

            Func <double, double> fnc = (double theta) => {
                double s         = 0.0;
                double uPrevious = 0.0;
                for (int i = 0; i < series.Count; i++)
                {
                    double u = series[i] - m - theta * uPrevious;
                    s        += u * u;
                    uPrevious = u;
                }
                ;
                return(s);
            };

            Extremum minimum = FunctionMath.FindMinimum(fnc, Interval.FromEndpoints(-1.0, 1.0));

            double beta   = minimum.Location;
            double sigma2 = minimum.Value / (n - 3);

            // While there is significant evidence that the MLE value for \beta is biased
            // for small-n, I know of no analytic correction.

            //double[] parameters = new double[] { beta, m, Math.Sqrt(sigma2) };

            // The calculation of the variance for \mu can be improved over the MLE
            // result by plugging the values for \gamma_0 and \gamma_1 into the
            // exact formula.

            double varBeta;

            if (minimum.Curvature > 0.0)
            {
                varBeta = sigma2 / minimum.Curvature;
            }
            else
            {
                varBeta = MoreMath.Sqr(1.0 - beta * beta) / n;
            }
            double varMu    = sigma2 * (MoreMath.Sqr(1.0 + beta) - 2.0 * beta / n) / n;
            double varSigma = sigma2 / 2.0 / n;

            List <double> residuals = new List <double>(n);
            double        u1        = 0.0;

            for (int i = 0; i < n; i++)
            {
                double u0 = series[i] - m - beta * u1;
                residuals.Add(u0);
                u1 = u0;
            }
            ;

            return(new MA1FitResult(
                       new UncertainValue(m, Math.Sqrt(varMu)),
                       new UncertainValue(beta, Math.Sqrt(varBeta)),
                       new UncertainValue(Math.Sqrt(sigma2), Math.Sqrt(varSigma)),
                       residuals
                       ));
        }
Exemple #7
0
        /// <summary>
        /// Fits an MA(1) model to the time series.
        /// </summary>
        /// <returns>The fit with parameters lag-1 coefficient, mean, and standard deviation.</returns>
        public FitResult FitToMA1()
        {
            if (data.Count < 4)
            {
                throw new InsufficientDataException();
            }

            // MA(1) model is
            //   y_t - \mu = u_t + \beta u_{t-1}
            // where u_t ~ N(0, \sigma) are IID.

            // It's easy to show that
            //   m = E(y_t) = \mu
            //   c_0 = V(y_t) = E((y_t - m)^2) = (1 + \beta^2) \sigma^2
            //   c_1 = V(y_t, y_{t-1}) = \beta \sigma^2
            // So the method of moments gives
            //   \beta = \frac{1 - \sqrt{1 - (2 g_1)^2}}{2}
            //   \mu = m
            //   \sigma^2 = \frac{c_0}{1 + \beta^2}
            // It turns out these are very poor (high bias, high variance) estimators,
            // but they illustrate a basic requirement that g_1 < 1/2.

            // The MLE estimator

            int n = data.Count;

            double m = data.Mean;

            Func <double, double> fnc = (double theta) => {
                double s         = 0.0;
                double uPrevious = 0.0;
                for (int i = 0; i < data.Count; i++)
                {
                    double u = data[i] - m - theta * uPrevious;
                    s        += u * u;
                    uPrevious = u;
                }
                ;
                return(s);
            };

            Extremum minimum = FunctionMath.FindMinimum(fnc, Interval.FromEndpoints(-1.0, 1.0));

            double beta   = minimum.Location;
            double sigma2 = minimum.Value / (data.Count - 3);

            // While there is significant evidence that the MLE value for \beta is biased
            // for small-n, I know of no anlytic correction.

            double[] parameters = new double[] { beta, m, Math.Sqrt(sigma2) };

            // The calculation of the variance for \mu can be improved over the MLE
            // result by plugging the values for \gamma_0 and \gamma_1 into the
            // exact formula.

            SymmetricMatrix covariances = new SymmetricMatrix(3);

            if (minimum.Curvature > 0.0)
            {
                covariances[0, 0] = sigma2 / minimum.Curvature;
            }
            else
            {
                covariances[0, 0] = MoreMath.Sqr(1.0 - beta * beta) / n;
            }
            covariances[1, 1] = sigma2 * (MoreMath.Sqr(1.0 + beta) - 2.0 * beta / n) / n;
            covariances[2, 2] = sigma2 / 2.0 / n;

            TimeSeries residuals = new TimeSeries();
            double     u1        = 0.0;

            for (int i = 0; i < data.Count; i++)
            {
                double u0 = data[i] - m - beta * u1;
                residuals.Add(u0);
                u1 = u0;
            }
            ;
            TestResult test = residuals.LjungBoxTest();

            FitResult result = new FitResult(
                parameters, covariances, test
                );

            return(result);
        }
Exemple #8
0
        public static void Optimization()
        {
            Interval range = Interval.FromEndpoints(0.0, 6.28);
            Extremum min   = FunctionMath.FindMinimum(x => Math.Sin(x), range);

            Console.WriteLine($"Found minimum of {min.Value} at {min.Location}.");
            Console.WriteLine($"Required {min.EvaluationCount} evaluations.");

            MultiExtremum rosenbrock = MultiFunctionMath.FindLocalMinimum(
                x => MoreMath.Sqr(2.0 - x[0]) + 100.0 * MoreMath.Sqr(x[1] - x[0] * x[0]),
                new ColumnVector(0.0, 0.0)
                );
            ColumnVector xm = rosenbrock.Location;

            Console.WriteLine($"Found minimum of {rosenbrock.Value} at ({xm[0]},{xm[1]}).");
            Console.WriteLine($"Required {rosenbrock.EvaluationCount} evaluations.");

            Func <IReadOnlyList <double>, double> leviFunction = z => {
                double x = z[0];
                double y = z[1];
                return(
                    MoreMath.Sqr(MoreMath.Sin(3.0 * Math.PI * x)) +
                    MoreMath.Sqr(x - 1.0) * (1.0 + MoreMath.Sqr(MoreMath.Sin(3.0 * Math.PI * y))) +
                    MoreMath.Sqr(y - 1.0) * (1.0 + MoreMath.Sqr(MoreMath.Sin(2.0 * Math.PI * y)))
                    );
            };

            Interval[] leviRegion = new Interval[] {
                Interval.FromEndpoints(-10.0, +10.0),
                Interval.FromEndpoints(-10.0, +10.0)
            };

            MultiExtremum levi = MultiFunctionMath.FindGlobalMinimum(leviFunction, leviRegion);

            ColumnVector zm = levi.Location;

            Console.WriteLine($"Found minimum of {levi.Value} at ({zm[0]},{zm[1]}).");
            Console.WriteLine($"Required {levi.EvaluationCount} evaluations.");

            // Select a dimension
            int n = 6;

            // Define a function that takes 2n polar coordinates in the form
            // phi_1, theta_1, phi_2, theta_2, ..., phi_n, theta_n, computes
            // the sum of the potential energy 1/d for all pairs.
            Func <IReadOnlyList <double>, double> thompson = (IReadOnlyList <double> v) => {
                double e = 0.0;
                // iterate over all distinct pairs of points
                for (int i = 0; i < n; i++)
                {
                    for (int j = 0; j < i; j++)
                    {
                        // compute the chord length between points i and j
                        double dx = Math.Cos(v[2 * j]) * Math.Cos(v[2 * j + 1]) - Math.Cos(v[2 * i]) * Math.Cos(v[2 * i + 1]);
                        double dy = Math.Cos(v[2 * j]) * Math.Sin(v[2 * j + 1]) - Math.Cos(v[2 * i]) * Math.Sin(v[2 * i + 1]);
                        double dz = Math.Sin(v[2 * j]) - Math.Sin(v[2 * i]);
                        double d  = Math.Sqrt(dx * dx + dy * dy + dz * dz);
                        e += 1.0 / d;
                    }
                }
                return(e);
            };

            // Limit all polar coordinates to their standard ranges.
            Interval[] box = new Interval[2 * n];
            for (int i = 0; i < n; i++)
            {
                box[2 * i]     = Interval.FromEndpoints(-Math.PI, Math.PI);
                box[2 * i + 1] = Interval.FromEndpoints(-Math.PI / 2.0, Math.PI / 2.0);
            }

            // Use settings to monitor proress toward a rough solution.
            MultiExtremumSettings roughSettings = new MultiExtremumSettings()
            {
                RelativePrecision = 0.05, AbsolutePrecision = 0.0,
                Listener          = r => {
                    Console.WriteLine($"Minimum {r.Value} after {r.EvaluationCount} evaluations.");
                }
            };
            MultiExtremum roughThompson = MultiFunctionMath.FindGlobalMinimum(thompson, box, roughSettings);

            // Use settings to monitor proress toward a refined solution.
            MultiExtremumSettings refinedSettings = new MultiExtremumSettings()
            {
                RelativePrecision = 1.0E-5, AbsolutePrecision = 0.0,
                Listener          = r => {
                    Console.WriteLine($"Minimum {r.Value} after {r.EvaluationCount} evaluations.");
                }
            };
            MultiExtremum refinedThompson = MultiFunctionMath.FindLocalMinimum(thompson, roughThompson.Location, refinedSettings);

            Console.WriteLine($"Minimum potential energy {refinedThompson.Value}.");
            Console.WriteLine($"Required {roughThompson.EvaluationCount} + {refinedThompson.EvaluationCount} evaluations.");

            /*
             * // Define a function that takes 2n coordinates x1, y1, x2, y2, ... xn, yn
             * // and finds the smallest distance between two coordinate pairs.
             * Func<IReadOnlyList<double>, double> function = (IReadOnlyList<double> x) => {
             *  double sMin = Double.MaxValue;
             *  for (int i = 0; i < n; i++) {
             *      for (int j = 0; j < i; j++) {
             *          double s = MoreMath.Hypot(x[2 * i] - x[2 * j], x[2 * i + 1] - x[2 * j + 1]);
             *          if (s < sMin) sMin = s;
             *      }
             *  }
             *  return (sMin);
             * };
             *
             * // Limit all coordinates to the unit box.
             * Interval[] box = new Interval[2 * n];
             * for (int i = 0; i < box.Length; i++) box[i] = Interval.FromEndpoints(0.0, 1.0);
             *
             * // Use settings to monitor proress toward a rough solution.
             * MultiExtremumSettings roughSettings = new MultiExtremumSettings() {
             *  RelativePrecision = 1.0E-2, AbsolutePrecision = 0.0,
             *  Listener = r => {
             *      Console.WriteLine($"Minimum {r.Value} after {r.EvaluationCount} evaluations.");
             *  }
             * };
             * MultiExtremum roughMaximum = MultiFunctionMath.FindGlobalMaximum(function, box, roughSettings);
             *
             * // Use settings to monitor proress toward a rough solution.
             * MultiExtremumSettings refinedSettings = new MultiExtremumSettings() {
             *  RelativePrecision = 1.0E-8, AbsolutePrecision = 0.0,
             *  Listener = r => {
             *      Console.WriteLine($"Minimum {r.Value} after {r.EvaluationCount} evaluations.");
             *  }
             * };
             * MultiExtremum refinedMaximum = MultiFunctionMath.FindLocalMaximum(function, roughMaximum.Location, refinedSettings);
             */
        }