All Downloads are FREE. Search and download functionalities are using the official Maven repository.

umontreal.iro.lecuyer.probdistmulti.BiNormalDonnellyDist Maven / Gradle / Ivy

Go to download

SSJ is a Java library for stochastic simulation, developed under the direction of Pierre L'Ecuyer, in the Département d'Informatique et de Recherche Opérationnelle (DIRO), at the Université de Montréal. It provides facilities for generating uniform and nonuniform random variates, computing different measures related to probability distributions, performing goodness-of-fit tests, applying quasi-Monte Carlo methods, collecting (elementary) statistics, and programming discrete-event simulations with both events and processes.

The newest version!


/*
 * Class:        BiNormalDonnellyDist
 * Description:  bivariate normal distribution using Donnelly's code
 * Environment:  Java
 * Software:     SSJ 
 * Organization: DIRO, Université de Montréal
 * @author       
 * @since
 */

package umontreal.iro.lecuyer.probdistmulti;

import umontreal.iro.lecuyer.probdist.NormalDist;
import umontreal.iro.lecuyer.util.Num;


/**
 * Extends the class {@link BiNormalDist} for the bivariate 
 *   normal distribution
 *   using a translation of Donnelly's FORTRAN code.
 * 
 */
public class BiNormalDonnellyDist extends BiNormalDist  {

private static final double TWOPI = 2.0*Math.PI;
private static final double SQRTPI = Math.sqrt(Math.PI);
private static final int KMAX = 6;

private static double BB[] = new double[KMAX + 1];
private static final double CB[] = {
    0.9999936, -0.9992989, 0.9872976,
   -0.9109973, 0.6829098, -0.3360210, 0.07612251 };


private static double BorthT (double h, double a)
{
   int k;
   final double w = a * h / Num.RAC2;
   final double w2 = w * w;
   double z = w * Math.exp(-w2);

   BB[0] = SQRTPI * (Gauss (Num.RAC2 * w) - 0.5);
   for (k = 1; k <= KMAX; ++k) {
      BB[k] = ((2 * k - 1) * BB[k - 1] - z) / 2.0;
      z *= w2;
   }

   final double h2 = h * h / 2.0;
   z = h / Num.RAC2;
   double sum = 0;
   for (k = 0; k <= KMAX; ++k) {
      sum += CB[k] * BB[k] / z;
      z *= h2;
   }
   return sum * Math.exp (-h2) / TWOPI;
}





   /**
    * Constructor with default  parameters
    *  
    * μ1 = μ2 = 0, 
    * σ1 = σ2 = 1, correlation
    *  ρ = rho, and d = ndig digits of accuracy 
    *  (the absolute error is smaller than 10-d). Restriction: d <= 15.
    * 
    */
   public BiNormalDonnellyDist (double rho, int ndig)  {
       super (rho);
       if (ndig > 15)
          throw new IllegalArgumentException ("ndig > 15");
       decPrec = ndigit = ndig;
   }


   /**
    * Same as {@link #BiNormalDonnellyDist(double,int) BiNormalDonnellyDist} (rho, 15).
    * 
    */
   public BiNormalDonnellyDist (double rho)  {
       this (rho, 15);
   }


   /**
    * Constructor with parameters
    *   μ1 = mu1, μ2 = mu2, σ1 = sigma1, 
    *  σ2 = sigma2, ρ = rho, and d = ndig
    *   digits of accuracy. Restriction: d <= 15.
    * 
    */
   public BiNormalDonnellyDist (double mu1, double sigma1, double mu2,
                                double sigma2, double rho, int ndig)  {
      super (mu1, sigma1, mu2, sigma2, rho);
      if (ndig > 15)
         throw new IllegalArgumentException ("ndig > 15");
      decPrec = ndigit = ndig;
   }


   /**
    * Same as {@link #BiNormalDonnellyDist(double,double,double,double,double,int) BiNormalDonnellyDist} (mu1, sigma1, mu2, sigma2, rho, 15).
    * 
    */
   public BiNormalDonnellyDist (double mu1, double sigma1, double mu2,
                                double sigma2, double rho)  {
      this (mu1, sigma1, mu2, sigma2, rho, 15);
   }


   /**
    * Computes the standard binormal distribution
    *   with the method described in, where ndig is the
    *   number of decimal digits of accuracy provided (ndig  <= 15).
    *   The code was translated from the Fortran program written by T. G. Donnelly
    *   and copyrighted by the ACM (see 
    *   http://www.acm.org/pubs/copyright_policy/#Notice). The absolute error
    *   is expected to be smaller than 10-d, where d = ndig.
    * 
    */
   public static double cdf (double x, double y, double rho, int ndig)  {
   /* 
    * This is a translation of the FORTRAN code published by Thomas G. Donnelly
    * in the CACM, Vol. 16, Number 10, p. 638, (1973)
    */

      if (ndig > 15)
         throw new IllegalArgumentException ("ndig > 15");
      double b = specialCDF (x, y, rho, 13.0);
      if (b >= 0.0)
         return b;
      b = 0;

      final boolean SINGLE_FLAG = ndig <= 7 ? true : false;
      final double TWO_PI = 2.0 * Math.PI;
      final double r = rho;
      final double ah = -x;
      final double ak = -y;

      double a2, ap, cn, conex, ex, g2, gh, gk, gw = 0, h2, h4, rr, s1, s2,
         sgn, sn, sp, sqr, t, temp, w2, wh = 0, wk = 0;
      int is = -1;

      if (SINGLE_FLAG) {
         gh = Gauss (x) / 2.0;
         gk = Gauss (y) / 2.0;
      } else {
         gh = NormalDist.cdf01 (x) / 2.0;
         gk = NormalDist.cdf01 (y) / 2.0;
      }
      boolean flag = true;    // Easiest way to translate a Fortran goto

      rr = (1 - r) * (1 + r);
      if (rr < 0)
         throw new IllegalArgumentException ("|rho| > 1");
      sqr = Math.sqrt(rr);
      final double con = Math.PI * Num.TEN_NEG_POW[ndig];
      final double EPSILON = 0.5*Num.TEN_NEG_POW[ndig];

      if (ah != 0) {
         b = gh;
         if (ah * ak < 0)
            b -= 0.5;
         else if (ah * ak == 0) {
            flag = false;
         }
      } else if (ak == 0) {
         return Math.asin (r) / TWO_PI + 0.25;
      }

      if (flag)
         b += gk;
      if (ah != 0) {
         flag = false;
         wh = -ah;
         wk = (ak / ah - r) / sqr;
         gw = 2 * gh;
         is = -1;
      }

      do {
         if (flag) {
            wh = -ak;
            wk = (ah / ak - r) / sqr;
            gw = 2 * gk;
            is = 1;
         }
         flag = true;
         sgn = -1;
         t = 0;
         if (wk != 0) {
            if (Math.abs (wk) >= 1)
               if (Math.abs (wk) == 1) {
                  t = wk * gw * (1 - gw) / 2;
                  b += sgn * t;
                  if (is >= 0)        // Another Fortran goto
                     break;
                  else
                     continue;
               } else {
                  sgn = -sgn;
                  wh = wh * wk;
                  if (SINGLE_FLAG)
                     g2 = Gauss(wh);
                  else
                     g2 = NormalDist.cdf01(wh);
                  wk = 1 / wk;
                  if (wk < 0)
                     b = b + .5;
                  b = b - (gw + g2) / 2 + gw * g2;
               }
        /* ****
            // Cette m'ethode de Borth est plus lente que simple Donnelly 
            if (ndig <= 7 && Math.abs (wh) > 1.6 && Math.abs (wk) > 0.3) {
               b += sgn * BorthT (wh, wk);
               if (is >= 0)
                  break;
               else
                  continue;
            }
        *****/
            h2 = wh * wh;
            a2 = wk * wk;
            h4 = h2 * .5;
            ex = 0;
            if (h4 < 300.0)
               ex = Math.exp (-h4);
            w2 = h4 * ex;
            ap = 1;
            s2 = ap - ex;
            sp = ap;
            s1 = 0;
            sn = s1;
            conex = Math.abs (con / wk);
            do {
               cn = ap * s2 / (sn + sp);
               s1 += cn;
               if (Math.abs (cn) <= conex)
                  break;
               sn = sp;
               sp += 1;
               s2 -= w2;
               w2 *= h4 / sp;
               ap = -ap * a2;
            } while (true);
            t = (Math.atan (wk) - wk * s1) / TWO_PI;
            b += sgn * t;
         }
         if (is >= 0)
            break;
      } while (ak != 0);

      if (b < EPSILON)
         b = 0;
      if (b > 1)
         b = 1;
      return b;
}


   /**
    * Computes the binormal distribution function with parameters μ1 = mu1,
    *  μ2 = mu2, σ1 = sigma1,  σ2 = sigma2,
    *  correlation ρ = rho and ndig decimal digits of accuracy.
    * 
    */
   public static double cdf (double mu1, double sigma1, double x, 
                             double mu2, double sigma2, double y,
                             double rho, int ndig)  {
      if (sigma1 <= 0)
         throw new IllegalArgumentException ("sigma1 <= 0");
      if (sigma2 <= 0)
         throw new IllegalArgumentException ("sigma2 <= 0");
      double X = (x - mu1)/sigma1;
      double Y = (y - mu2)/sigma2;
      return cdf (X, Y, rho, ndig);
   }


   /**
    * Computes the upper binormal distribution function  with parameters μ1 = mu1,
    *  μ2 = mu2, σ1 = sigma1,  σ2 = sigma2,
    *  ρ = rho and ndig decimal digits of accuracy.
    * 
    */
   public static double barF (double mu1, double sigma1, double x, 
                              double mu2, double sigma2, double y,
                              double rho, int ndig)  {
      if (sigma1 <= 0)
         throw new IllegalArgumentException ("sigma1 <= 0");
      if (sigma2 <= 0)
         throw new IllegalArgumentException ("sigma2 <= 0");
      double X = (x - mu1)/sigma1;
      double Y = (y - mu2)/sigma2;
      return barF (X, Y, rho, ndig);
   }


   /**
    * Computes the upper standard binormal distribution function  with parameters ρ = rho and 
    *    ndig decimal digits of accuracy.
    * 
    */
   public static double barF (double x, double y, double rho, int ndig)   {
      return cdf (-x, -y, rho, ndig);
   }
  

   public double cdf (double x, double y) {
      return cdf ((x-mu1)/sigma1, (y-mu2)/sigma2, rho, ndigit);
   }

   public static double cdf (double x, double y, double rho) {
       return cdf (x, y, rho, 15);
   }
 
   public static double cdf (double mu1, double sigma1, double x, 
                             double mu2, double sigma2, double y,
                             double rho) {
      return cdf (mu1, sigma1, x, mu2, sigma2, y, rho, 15);
   }

   public double barF (double x, double y) {
      return barF ((x-mu1)/sigma1, (y-mu2)/sigma2, rho, ndigit);
   }

   public static double barF (double mu1, double sigma1, double x, 
                              double mu2, double sigma2, double y,
                              double rho) {
      return barF (mu1, sigma1, x, mu2, sigma2, y, rho, 15);
   }

   public static double barF (double x, double y, double rho) {
      return barF (x, y, rho, 15);
   }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy