示例#1
0
        // Reference:
        // "Computation of the incomplete gamma function ratios and their inverse"
        // Armido R DiDonato and Alfred H Morris, Jr.
        // ACM Transactions on Mathematical Software (TOMS)
        // Volume 12 Issue 4, Dec. 1986
        // http://dl.acm.org/citation.cfm?id=23109
        // and section 8.3 of
        // "Numerical Methods for Special Functions"
        // Amparo Gil, Javier Segura, Nico M. Temme, 2007
        /// <summary>
        /// Compute the regularized lower incomplete Gamma function: <c>int_0^x t^(a-1) exp(-t) dt / Gamma(a)</c>
        /// </summary>
        /// <param name="a">Must be &gt; 20</param>
        /// <param name="x"></param>
        /// <param name="upper">If true, compute the upper incomplete Gamma function</param>
        /// <returns></returns>
        private static double GammaAsympt(double a, double x, bool upper)
        {
            if (a <= 20)
            {
                throw new Exception("a <= 20");
            }
            double xOverAMinus1 = (x - a) / a;
            double phi          = xOverAMinus1 - MMath.Log1Plus(xOverAMinus1);
            double y            = a * phi;
            double z            = Math.Sqrt(2 * phi);

            if (x <= a)
            {
                z *= -1;
            }
            double[] b = new double[GammaLowerAsympt_C0.Length];
            b[b.Length - 1] = GammaLowerAsympt_C0[b.Length - 1];
            double sum = b[b.Length - 1];

            b[b.Length - 2] = GammaLowerAsympt_C0[b.Length - 2];
            sum             = z * sum + b[b.Length - 2];
            for (int i = b.Length - 3; i >= 0; i--)
            {
                b[i] = b[i + 2] * (i + 2) / a + GammaLowerAsympt_C0[i];
                sum  = z * sum + b[i];
            }
            sum *= a / (a + b[1]);
            if (x <= a)
            {
                sum *= -1;
            }
            double result = 0.5 * Erfc(Math.Sqrt(y)) + sum * Math.Exp(-y) / (Math.Sqrt(a) * MMath.Sqrt2PI);

            return(((x > a) == upper) ? result : 1 - result);
        }
示例#2
0
        /// <summary>
        /// Sample from a Gaussian(0,1) truncated at the given upper and lower bounds
        /// </summary>
        /// <param name="lowerBound">Can be -Infinity.</param>
        /// <param name="upperBound">Must be &gt;= <paramref name="lowerBound"/>.  Can be Infinity.</param>
        /// <returns>A real number &gt;= <paramref name="lowerBound"/> and &lt; <paramref name="upperBound"/></returns>
        public static double NormalBetween(double lowerBound, double upperBound, IPolyrand random = null)
        {
            if (double.IsNaN(lowerBound))
            {
                throw new ArgumentException("lowerBound is NaN");
            }
            if (double.IsNaN(upperBound))
            {
                throw new ArgumentException("upperBound is NaN");
            }
            double delta = upperBound - lowerBound;

            if (delta == 0)
            {
                return(lowerBound);
            }
            if (delta < 0)
            {
                throw new ArgumentException("upperBound (" + upperBound + ") < lowerBound (" + lowerBound + ")");
            }
            // Switch between the following 3 options:
            // 1. Gaussian rejection, with acceptance rate Z = NormalCdf(upperBound) - NormalCdf(lowerBound)
            // 2. Uniform rejection, with acceptance rate sqrt(2*pi)*Z/delta if the interval contains 0
            // 3. Truncated exponential rejection, with acceptance rate
            //    = sqrt(2*pi)*Z*lambda*exp(-lambda^2/2)/(exp(-lambda*lowerBound)-exp(-lambda*upperBound))
            //    = sqrt(2*pi)*Z*lowerBound*exp(lowerBound^2/2)/(1-exp(-lowerBound*(upperBound-lowerBound)))
            // (3) has the highest acceptance rate under the following conditions:
            //     lowerBound > 0.5 or (lowerBound > 0 and delta < 2.5)
            // (2) has the highest acceptance rate if the interval contains 0 and delta < sqrt(2*pi)
            // (1) has the highest acceptance rate otherwise
            if (lowerBound > 0.5 || (lowerBound > 0 && delta < 2.5))
            {
                // Rejection sampler using truncated exponential proposal
                double lambda = lowerBound;
                double s      = MMath.ExpMinus1(-lambda * delta);
                double c      = 2 * lambda * lambda;
                while (true)
                {
                    double x = -MMath.Log1Plus(s * NextDouble(random));
                    double u = -System.Math.Log(NextDouble(random));
                    if (c * u > x * x)
                    {
                        return(x / lambda + lowerBound);
                    }
                }
                throw new Exception("failed to sample");
            }
            else if (upperBound < -0.5 || (upperBound < 0 && delta < 2.5))
            {
                return(-NormalBetween(-upperBound, -lowerBound));
            }
            else if (lowerBound <= 0 && upperBound >= 0 && delta < MMath.Sqrt2PI)
            {
                // Uniform rejection
                while (true)
                {
                    double x = NextDouble(random) * delta + lowerBound;
                    double u = -System.Math.Log(NextDouble(random));
                    if (2 * u > x * x)
                    {
                        return(x);
                    }
                }
            }
            else
            {
                // Gaussian rejection
                while (true)
                {
                    double x = Rand.Normal(random);
                    if (x >= lowerBound && x < upperBound)
                    {
                        return(x);
                    }
                }
            }
        }