예제 #1
0
        /// <summary>
        /// Computes <c>1/Gamma(x+1) - 1</c> to high accuracy
        /// </summary>
        /// <param name="x">A real number &gt;= 0</param>
        /// <returns></returns>
        public static double ReciprocalFactorialMinus1(double x)
        {
            if (x > 0.3)
            {
                return(1 / MMath.Gamma(x + 1) - 1);
            }
            double sum  = 0;
            double term = x;

            for (int i = 0; i < reciprocalFactorialMinus1Coeffs.Length; i++)
            {
                sum  += reciprocalFactorialMinus1Coeffs[i] * term;
                term *= x;
            }
            return(sum);
        }
예제 #2
0
 /// <summary>
 /// Computes <c>GammaLn(x) - (x-0.5)*log(x) + x - 0.5*log(2*pi)</c> for x &gt;= 10
 /// </summary>
 /// <param name="x">A real number &gt;= 10</param>
 /// <returns></returns>
 private static double GammaLnSeries(double x)
 {
     // GammaLnSeries(10) = 0.008330563433362871
     if (x < 10)
     {
         return(MMath.GammaLn(x) - (x - 0.5) * Math.Log(x) + x - LnSqrt2PI);
     }
     else
     {
         // the series is:  sum_{i=1}^inf B_{2i} / (2i*(2i-1)*x^(2i-1))
         double sum   = 0;
         double term  = 1.0 / x;
         double delta = term * term;
         for (int i = 0; i < c_gammaln_series.Length; i++)
         {
             sum  += c_gammaln_series[i] * term;
             term *= delta;
         }
         return(sum);
     }
 }
예제 #3
0
        /// <summary>
        /// Compute the regularized upper incomplete Gamma function by a series expansion
        /// </summary>
        /// <param name="a">The shape parameter, &gt; 0</param>
        /// <param name="x">The lower bound of the integral, &gt;= 0</param>
        /// <returns></returns>
        private static double GammaUpperSeries(double a, double x)
        {
            // this series should only be applied when x is small
            // the series is: 1 - x^a sum_{k=0}^inf (-x)^k /(k! Gamma(a+k+1))
            // = (1 - 1/Gamma(a+1)) + (1 - x^a)/Gamma(a+1) - x^a sum_{k=1}^inf (-x)^k/(k! Gamma(a+k+1))
            double xaMinus1 = MMath.ExpMinus1(a * Math.Log(x));
            double aReciprocalFactorial, aReciprocalFactorialMinus1;

            if (a > 0.3)
            {
                aReciprocalFactorial       = 1 / MMath.Gamma(a + 1);
                aReciprocalFactorialMinus1 = aReciprocalFactorial - 1;
            }
            else
            {
                aReciprocalFactorialMinus1 = ReciprocalFactorialMinus1(a);
                aReciprocalFactorial       = 1 + aReciprocalFactorialMinus1;
            }
            // offset = 1 - x^a/Gamma(a+1)
            double offset = -xaMinus1 * aReciprocalFactorial - aReciprocalFactorialMinus1;
            double scale  = 1 - offset;
            double term   = x / (a + 1) * a;
            double sum    = term;

            for (int i = 1; i < 1000; i++)
            {
                term *= -(a + i) * x / ((a + i + 1) * (i + 1));
                double sumOld = sum;
                sum += term;
                //Console.WriteLine("{0}: {1}", i, sum);
                if (AreEqual(sum, sumOld))
                {
                    return(scale * sum + offset);
                }
            }
            throw new Exception(string.Format("GammaUpperSeries not converging for a={0} x={1}", a, x));
        }
예제 #4
0
        private static double RepresentationMidpoint(double lower, double upper)
        {
            if (lower == 0)
            {
                if (upper < 0)
                {
                    return(-RepresentationMidpoint(-lower, -upper));
                }
                else if (upper == 0)
                {
                    return(lower);
                }
                // fall through
            }
            else if (lower < 0)
            {
                if (upper <= 0)
                {
                    return(-RepresentationMidpoint(-lower, -upper));
                }
                else
                {
                    return(0); // upper > 0
                }
            }
            else if (upper < 0)
            {
                return(0);                // lower > 0
            }
            // must have lower >= 0, upper >= 0
            long lowerBits = BitConverter.DoubleToInt64Bits(lower);
            long upperBits = BitConverter.DoubleToInt64Bits(upper);
            long midpoint  = MMath.Average(lowerBits, upperBits);

            return(BitConverter.Int64BitsToDouble(midpoint));
        }
예제 #5
0
        public static int Poisson(double mean, IPolyrand random = null)
        {
            // TODO: There are more efficient samplers
            if (mean < 0)
            {
                throw new ArgumentException("mean < 0");
            }
            else if (mean == 0.0)
            {
                return(0);
            }
            else if (mean < 10)
            {
                double L = System.Math.Exp(-mean);
                double p = 1.0;
                int    k = 0;
                do
                {
                    k++;
                    p *= NextDouble(random);
                } while (p > L);
                return(k - 1);
            }
            else
            {
                // mean >= 10
                // Devroye ch10.3, with corrections
                // Reference: "Non-Uniform Random Variate Generation" by Luc Devroye (1986)
                double mu        = System.Math.Floor(mean);
                double muLogFact = MMath.GammaLn(mu + 1);
                double logMeanMu = System.Math.Log(mean / mu);
                double delta     = System.Math.Max(6, System.Math.Min(mu, System.Math.Sqrt(2 * mu * System.Math.Log(128 * mu / System.Math.PI))));
                double c1        = System.Math.Sqrt(System.Math.PI * mu / 2);
                double c2        = c1 + System.Math.Sqrt(System.Math.PI * (mu + delta / 2) / 2) * System.Math.Exp(1 / (2 * mu + delta));
                double c3        = c2 + 2;
                double c4        = c3 + System.Math.Exp(1.0 / 78);
                double c         = c4 + 2 / delta * (2 * mu + delta) * System.Math.Exp(-delta / (2 * mu + delta) * (1 + delta / 2));
                while (true)
                {
                    double u = NextDouble(random) * c;
                    double x, w;
                    if (u <= c1)
                    {
                        double n = Rand.Normal(random);
                        double y = -System.Math.Abs(n) * System.Math.Sqrt(mu) - 1;
                        x = System.Math.Floor(y);
                        if (x < -mu)
                        {
                            continue;
                        }
                        w = -n * n / 2;
                    }
                    else if (u <= c2)
                    {
                        double n = Rand.Normal(random);
                        double y = 1 + System.Math.Abs(n) * System.Math.Sqrt(mu + delta / 2);
                        x = System.Math.Ceiling(y);
                        if (x > delta)
                        {
                            continue;
                        }
                        w = (2 - y) * y / (2 * mu + delta);
                    }
                    else if (u <= c3)
                    {
                        x = 0;
                        w = 0;
                    }
                    else if (u <= c4)
                    {
                        x = 1;
                        w = 0;
                    }
                    else
                    {
                        double v = -System.Math.Log(NextDouble(random));
                        double y = delta + v * 2 / delta * (2 * mu + delta);
                        x = System.Math.Ceiling(y);
                        w = -delta / (2 * mu + delta) * (1 + y / 2);
                    }

                    double e = -System.Math.Log(NextDouble(random));
                    w -= e + x * logMeanMu;
                    double qx = x * System.Math.Log(mu) - MMath.GammaLn(mu + x + 1) + muLogFact;
                    if (w <= qx)
                    {
                        return((int)System.Math.Round(x + mu));
                    }
                }
            }
        }
예제 #6
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);
                    }
                }
            }
        }