예제 #1
0
        /// <summary>
        /// Computes <c>int_0^infinity t^n N(t;x,1) dt / (n! N(x;0,1))</c>
        /// </summary>
        /// <param name="n">The exponent</param>
        /// <param name="x">A real number &gt; -1</param>
        /// <returns></returns>
        private static double NormalCdfMomentRatioRecurrence(int n, double x)
        {
            double rPrev = MMath.NormalCdfRatio(x);

            if (n == 0)
            {
                return(rPrev);
            }
            double r = x * rPrev + 1;

            for (int i = 1; i < n; i++)
            {
                double rNew = (x * r + rPrev) / (i + 1);
                rPrev = r;
                r     = rNew;
            }
            return(r);
        }
예제 #2
0
        // Helper function for NormalCdfRatioConFrac
        private static double NormalCdfRatioConFracNumer(double x, double y, double r, double scale, double sqrtomr2, double diff, double Rdiff)
        {
            double delta = (1 + r) * (y - x) / sqrtomr2;
            double numer;

            if (Math.Abs(delta) > 0.5)
            {
                numer = scale * r * Rdiff;
                double diffy = (y - r * x) / sqrtomr2;
                if (scale == 1)
                {
                    numer += MMath.NormalCdfRatio(diffy);
                }
                else // this assumes scale = N((y-rx)/sqrt(1-r^2);0,1)
                {
                    numer += MMath.NormalCdf(diffy);
                }
            }
            else
            {
                numer = scale * (NormalCdfRatioDiff(diff, delta) + (1 + r) * Rdiff);
            }
            return(numer);
        }
예제 #3
0
 /// <summary>
 /// Returns NormalCdfMomentRatio(i,x) for i=n,n+1,n+2,...
 /// </summary>
 /// <param name="n">A starting index &gt;= 0</param>
 /// <param name="x">A real number</param>
 /// <param name="useConFrac">If true, do not use the lookup table</param>
 /// <returns></returns>
 private static IEnumerator <double> NormalCdfMomentRatioSequence(int n, double x, bool useConFrac = false)
 {
     if (n < 0)
     {
         throw new ArgumentException("n < 0");
     }
     if (x > -1)
     {
         // Use the upward recurrence.
         double rPrev = MMath.NormalCdfRatio(x);
         if (n == 0)
         {
             yield return(rPrev);
         }
         double r = x * rPrev + 1;
         if (n <= 1)
         {
             yield return(r);
         }
         for (int i = 1; ; i++)
         {
             double rNew = (x * r + rPrev) / (i + 1);
             rPrev = r;
             r     = rNew;
             if (n <= i + 1)
             {
                 yield return(r);
             }
         }
     }
     else
     {
         // Use the downward recurrence.
         // Each batch of tableSize items is generated in advance.
         // If tableSize is larger than 50, the results will lose accuracy.
         // If tableSize is too small, then the recurrence is used less often, requiring more computation.
         int maxTableSize = 30;
         // rtable[tableStart-i] = R_i
         double[] rtable     = new double[maxTableSize];
         int      tableStart = -1;
         int      tableSize  = 2;
         for (int i = n; ; i++)
         {
             if (i > tableStart)
             {
                 // build the table
                 tableStart = i + tableSize - 1;
                 if (useConFrac)
                 {
                     rtable[0] = MMath.NormalCdfMomentRatioConFrac(tableStart, x);
                     rtable[1] = MMath.NormalCdfMomentRatioConFrac(tableStart - 1, x);
                 }
                 else
                 {
                     rtable[0] = MMath.NormalCdfMomentRatio(tableStart, x);
                     rtable[1] = MMath.NormalCdfMomentRatio(tableStart - 1, x);
                 }
                 for (int j = 2; j < tableSize; j++)
                 {
                     int nj = tableStart - j + 1;
                     if (rtable[j - 2] == 0 && rtable[j - 1] == 0)
                     {
                         rtable[j] = MMath.NormalCdfMomentRatio(nj - 1, x);
                     }
                     else
                     {
                         rtable[j] = (nj + 1) * rtable[j - 2] - x * rtable[j - 1];
                     }
                 }
                 // Increase tableSize up to the maximum.
                 tableSize = Math.Min(maxTableSize, 2 * tableSize);
             }
             yield return(rtable[tableStart - i]);
         }
     }
 }
예제 #4
0
        // Returns NormalCdf divided by N(x;0,1) N((y-rx)/sqrt(1-r^2);0,1)
        // requires -1 < x < 0, abs(r) <= 0.6, and x-r*y <= 0 (or equivalently y < -x).
        // Uses Taylor series at r=0.
        private static double NormalCdfRatioTaylor(double x, double y, double r)
        {
            if (Math.Abs(x) > 5 || Math.Abs(y) > 5)
            {
                throw new ArgumentOutOfRangeException();
            }
            // First term of the Taylor series
            double sum      = MMath.NormalCdfRatio(x) * MMath.NormalCdfRatio(y);
            double Halfx2y2 = (x * x + y * y) / 2;
            double xy       = x * y;
            // Q = NormalCdf(x,y,r)/dNormalCdf(x,y,r)
            // where dNormalCdf(x,y,r) = N(x;0,1) N(y; rx, 1-r^2)
            List <double> Qderivs = new List <double>();

            Qderivs.Add(sum);
            List <double> logphiDerivs = new List <double>();
            double        dlogphi      = xy;

            logphiDerivs.Add(dlogphi);
            // dQ(0) = 1 - Q(0) d(log(dNormalCdf))
            double Qderiv = 1 - sum * dlogphi;

            Qderivs.Add(Qderiv);
            double rPowerN = r;

            // Second term of the Taylor series
            sum += Qderiv * rPowerN;
            double sumOld = sum;

            for (int n = 2; n <= 100; n++)
            {
                if (n == 100)
                {
                    throw new Exception($"not converging for x={x}, y={y}, r={r}");
                }
                //Console.WriteLine($"n = {n - 1} sum = {sum:r}");
                double dlogphiOverFactorial;
                if (n % 2 == 0)
                {
                    dlogphiOverFactorial = 1.0 / n - Halfx2y2;
                }
                else
                {
                    dlogphiOverFactorial = xy;
                }
                logphiDerivs.Add(dlogphiOverFactorial);
                // ddQ = -dQ d(log(dNormalCdf)) - Q dd(log(dNormalCdf)), and so on
                double QderivOverFactorial = 0;
                for (int i = 0; i < n; i++)
                {
                    QderivOverFactorial -= Qderivs[i] * logphiDerivs[n - i - 1] * (n - i) / n;
                }
                Qderivs.Add(QderivOverFactorial);
                rPowerN *= r;
                sum     += QderivOverFactorial * rPowerN;
                if ((sum > double.MaxValue) || double.IsNaN(sum))
                {
                    throw new Exception($"not converging for x={x}, y={y}, r={r}");
                }
                if (AreEqual(sum, sumOld))
                {
                    break;
                }
                sumOld = sum;
            }
            double omr2 = (1 - r) * (1 + r); // more accurate than 1-r*r

            return(sum / Math.Sqrt(omr2));
        }