def rinhomexpo_cum(self, lamt, suplamt): """ Generates - cumulatively - inhomogeneously exponentially distributed interarrival times (due to an inhomogeneous Poisson process) from clock time = 0.0 (the algorithm is taken from Bratley, Fox & Schrage). 'lamt' is an externally defined function of clock time that returns the present arrival rate (arrival frequency). 'suplamt' is another externally defined function that returns the supremum of the arrival rate over the REMAINING time from the present clock time. NB. A SEPARATE STREAM MUST BE INSTANTIATED FOR EACH ONE OF THE VARIATES THAT ARE GENERATED USING THIS METHOD, EACH WITH A SEPARATE SEED!! """ errtxt1 = "All values from suplamt must be " errtxt1 += "non-negative floats in rinhomexpo_cum!" errtxt2 = "All values from lamt must be " errtxt2 += "non-negative floats in rinhomexpo_cum!" ticum = self.__ticum while True: lamsup = suplamt(ticum) assert lamsup >= 0.0, errortxt1 u = self.runif01() ticum -= safediv(1.0, lamsup) * safelog(self.runif01()) lam = lamt(ticum) assert lam >= 0.0, errortxt2 if u * lamsup <= lam: break self.__ticum = ticum return ticum
def rinhomexpo_cum(self, lamt, suplamt): """ Generates - cumulatively - inhomogeneously exponentially distributed interarrival times (due to an inhomogeneous Poisson process) from clock time = 0.0 (the algorithm is taken from Bratley, Fox & Schrage). 'lamt' is an externally defined function of clock time that returns the present arrival rate (arrival frequency). 'suplamt' is another externally defined function that returns the supremum of the arrival rate over the REMAINING time from the present clock time. NB. A SEPARATE STREAM MUST BE INSTANTIATED FOR EACH ONE OF THE VARIATES THAT ARE GENERATED USING THIS METHOD, EACH WITH A SEPARATE SEED!! """ errtxt1 = "All values from suplamt must be " errtxt1 += "non-negative floats in rinhomexpo_cum!" errtxt2 = "All values from lamt must be " errtxt2 += "non-negative floats in rinhomexpo_cum!" ticum = self.__ticum while True: lamsup = suplamt(ticum) assert lamsup >= 0.0, errortxt1 u = self.runif01() ticum -= safediv(1.0, lamsup) * safelog(self.runif01()) lam = lamt(ticum) assert lam >= 0.0, errortxt2 if u*lamsup <= lam: break self.__ticum = ticum return ticum
def ilogistic(prob, mu=0.0, scale=1.0): """ The inverse of the logistic distribution: f = exp[-(x-m)/s] / (s*{1 + exp[-(x-m)/s]}**2) F = 1 / {1 + exp[-(x-m)/s]} x in R m is the mean and mode, s is a scale parameter (s >= 0) """ _assertprob(prob, 'ilogistic') assert scale >= 0.0, "scale parameter in ilogistic must not be negative!" x = mu - scale*safelog(safediv(1.0, prob) - 1.0) return x
def rpiecexpo_cum(self, times, lamt): """ Generates - cumulatively - piecewise exponentially distributed interarrival times from clock time = 0.0 (the algorithm is taken from Bratley, Fox & Schrage). 'times' is a list or tuple containing the points at which the arrival rate (= arrival frequency) changes (the first break time point must be 0.0). 'lamt' is a list or tuple containing the arrival rates between break points. The number of elements in 'times' must be one more than the number of elements in 'lamt'! The algorithm cranking out the numbers is cyclic - the procedure starts over from time zero when the last break point is reached. THE PREVENT THE RESTART FROM TAKING PLACE, A (VERY) LARGE NUMBER MUST BE GIVEN AS THE LAST BREAK POINT (the cyclicity is rarely needed or desired in practice). NB. A SEPARATE STREAM MUST BE INSTANTIATED FOR EACH ONE OF THE VARIATES THAT ARE GENERATED USING THIS METHOD, EACH WITH A SEPARATE SEED!! """ ntimes = len(times) errtxt1 = "No. of arrival rates and no. of break points " errtxt2 = "are incompatible in rpiecexpo_cum!" errtext = errtxt1 + errtxt2 assert len(lamt) == ntimes-1, errtext r = ntimes*[0.0] for k in range(1, ntimes): assert lamt[k-1] >= 0.0, \ "All lamt must be non-negative floats in rpiecexpo_cum!" r[k] = r[k-1] + lamt[k-1] * (times[k]-times[k-1]) cumul = self.__cumul cumul -= safelog(self.runif01()) iseg = 0 while r[iseg+1] <= cumul: iseg = iseg + 1 tcum = times[iseg] + safediv(cumul-r[iseg], lamt[iseg]) tcum = kept_within(0.0, tcum) self.__cumul = cumul # NB THIS IS NOT CUMULATIVE TIME! return tcum
def rexpo_cum(self, lam): """ Generates - cumulatively - exponentially distributed interarrival times with arrival rate (arrivale frequency) 'lam' from clock time = 0.0. NB. A SEPARATE STREAM MUST BE INSTANTIATED FOR EACH ONE OF THE VARIATES THAT ARE GENERATED USING THIS METHOD, EACH WITH A SEPARATE SEED!! """ assert lam >= 0.0, \ "Arrival rate must be a non-negative float in rexpo_cum!" tcum = self.__tcum tcum -= safediv(1.0, lam) * safelog(self.runif01()) self.__tcum = tcum return tcum
def rpiecexpo_cum(self, times, lamt): """ Generates - cumulatively - piecewise exponentially distributed interarrival times from clock time = 0.0 (the algorithm is taken from Bratley, Fox & Schrage). 'times' is a list or tuple containing the points at which the arrival rate (= arrival frequency) changes (the first break time point must be 0.0). 'lamt' is a list or tuple containing the arrival rates between break points. The number of elements in 'times' must be one more than the number of elements in 'lamt'! The algorithm cranking out the numbers is cyclic - the procedure starts over from time zero when the last break point is reached. THE PREVENT THE RESTART FROM TAKING PLACE, A (VERY) LARGE NUMBER MUST BE GIVEN AS THE LAST BREAK POINT (the cyclicity is rarely needed or desired in practice). NB. A SEPARATE STREAM MUST BE INSTANTIATED FOR EACH ONE OF THE VARIATES THAT ARE GENERATED USING THIS METHOD, EACH WITH A SEPARATE SEED!! """ ntimes = len(times) errtxt1 = "No. of arrival rates and no. of break points " errtxt2 = "are incompatible in rpiecexpo_cum!" errtext = errtxt1 + errtxt2 assert len(lamt) == ntimes - 1, errtext r = ntimes * [0.0] for k in range(1, ntimes): assert lamt[k-1] >= 0.0, \ "All lamt must be non-negative floats in rpiecexpo_cum!" r[k] = r[k - 1] + lamt[k - 1] * (times[k] - times[k - 1]) cumul = self.__cumul cumul -= safelog(self.runif01()) iseg = 0 while r[iseg + 1] <= cumul: iseg = iseg + 1 tcum = times[iseg] + safediv(cumul - r[iseg], lamt[iseg]) tcum = kept_within(0.0, tcum) self.__cumul = cumul # NB THIS IS NOT CUMULATIVE TIME! return tcum
def rconst_cum(self, lamc): """ Generates - cumulatively - constantly spaced interarrival times with arrival rate (arrival frequency) lamc from clock time = 0.0. NB A dummy random number is picked each time the method is called for reasons of synchronization. NB. A SEPARATE STREAM MUST BE INSTANTIATED FOR EACH ONE OF THE VARIATES THAT ARE GENERATED USING THIS METHOD, EACH WITH A SEPARATE SEED!! """ assert lamc >= 0.0, \ "Arrival rate must be a non-negative float in rconst_cum!" tcum = self.__tcum tcum += safediv(1.0, lamc) self.__tcum = tcum return tcum
def rgamma(self, alpha, lam, xmax=float('inf')): """ The gamma distribution: f = lam * exp(-lam*x) * (lam*x)**(alpha-1) / gamma(alpha) The cdf is the integral = the incomplete gamma ratio. x, alpha >= 0; lam > 0.0 The generator is a slight modification of Python's built-in "gammavariate". """ assert alpha >= 0.0, "alpha must be non-negative in rgamma!" assert lam > 0.0, "lambda must be a positive float in rgamma!" assert xmax >= 0.0, \ "variate max must be a non-negative float in rgamma!" f, i = modf(alpha) if f == 0.0: if 1.0 <= i and i <= GeneralRandomStream.__GAMMA2ERLANG: return self.rerlang(int(i), 1.0 / lam, xmax) if alpha < 1.0: # Uses ALGORITHM GS of Statistical Computing - Kennedy & Gentle # (according to Python's "gammavariate") alphainv = 1.0 / alpha alpham1 = alpha - 1.0 while True: while True: u = self.runif01() b = (E + alpha) / E p = b * u if p <= 1.0: w = p**alphainv else: w = -safelog((b - p) * alphainv) u1 = self.runif01() if p > 1.0: if u1 <= w**(alpham1): break elif u1 <= exp(-w): break x = w / lam if x <= xmax: break else: # elif alpha > 1.0: # Uses R.C.H. Cheng, "The generation of Gamma # variables with non-integral shape parameters", # Applied Statistics, (1977), 26, No. 1, p71-74 # (according to Python's "gammavariate") ainv = sqrt(2.0 * alpha - 1.0) beta = 1.0 / ainv bbb = alpha - GeneralRandomStream.__LN4 ccc = alpha + ainv while True: while True: u1 = self.runif01() u2 = self.runif01() v = beta * safelog(safediv(u1, 1.0 - u1)) w = alpha * exp(v) c1 = u1 * u1 * u2 r = bbb + ccc * v - w c2 = r + 2.5040773967762742 - 4.5 * c1 # 2.5040773967762742 = 1.0 + log(4.5) if c2 >= 0.0 or r >= safelog(c1): break x = w / lam if x <= xmax: break x = kept_within(0.0, x) return x
def rbeta(self, a, b, x1, x2): """ The beta distribution f = x**(a-1) * (1-x)**(b-1) / beta(a, b) The cdf is the integral = the incomplete beta or the incomplete beta/complete beta depending on how the incomplete beta function is defined. x, a, b >= 0; x2 > x1 The algorithm is due to Berman (1970)/Jonck (1964) (for a and b < 1.0) and Cheng (1978) as described in Bratley, Fox and Schrage. """ assert a > 0.0, \ "shape parameters a and b must both be > 0.0 in rbeta!" assert b > 0.0, \ "shape parameters a and b must both be > 0.0 in rbeta!" assert x2 >= x1, \ "support span must not be negative in rbeta!" if x2 == x1: return x1 if a == 1.0 and b == 1.0: y = self.runif01() elif is_posinteger(a) and is_posinteger(b): nstop = int(a + b - 1.0) r = [] for k in range(0, nstop): r.append(self.runif01()) r.sort() y = r[int(a - 1.0)] elif a < 1.0 and b < 1.0: a1 = 1.0 / a b1 = 1.0 / b while True: u = pow(self.runif01(), a1) v = pow(self.runif01(), b1) if u + v <= 1.0: y = u / (u + v) break else: alpha = a + b if min(a, b) <= 1.0: beta = 1.0 / min(a, b) else: beta = sqrt((alpha - 2.0) / (2.0 * a * b - alpha)) gamma = a + 1.0 / beta while True: u1 = self.runif01() u2 = self.runif01() u1 = kept_within(TINY, u1, ONEMMACHEPS) u2 = kept_within(TINY, u2, HUGE) comp1 = safelog(u1 * u1 * u2) v = beta * safelog(safediv(u1, 1.0 - u1)) w = a * exp(v) comp2 = alpha*safelog(alpha/(b+w)) + gamma*v - \ GeneralRandomStream.__LN4 if comp2 >= comp1: y = w / (b + w) break x = y * (x2 - x1) + x1 x = kept_within(x1, x, x2) return x
def rgamma(self, alpha, lam, xmax=float('inf')): """ The gamma distribution: f = lam * exp(-lam*x) * (lam*x)**(alpha-1) / gamma(alpha) The cdf is the integral = the incomplete gamma ratio. x, alpha >= 0; lam > 0.0 The generator is a slight modification of Python's built-in "gammavariate". """ assert alpha >= 0.0, "alpha must be non-negative in rgamma!" assert lam > 0.0, "lambda must be a positive float in rgamma!" assert xmax >= 0.0, \ "variate max must be a non-negative float in rgamma!" f, i = modf(alpha) if f == 0.0: if 1.0 <= i and i <= GeneralRandomStream.__GAMMA2ERLANG: return self.rerlang(int(i), 1.0/lam, xmax) if alpha < 1.0: # Uses ALGORITHM GS of Statistical Computing - Kennedy & Gentle # (according to Python's "gammavariate") alphainv = 1.0 / alpha alpham1 = alpha - 1.0 while True: while True: u = self.runif01() b = (E+alpha) / E p = b * u if p <= 1.0: w = p ** alphainv else: w = -safelog((b-p)*alphainv) u1 = self.runif01() if p > 1.0: if u1 <= w ** (alpham1): break elif u1 <= exp(-w): break x = w / lam if x <= xmax: break else: # elif alpha > 1.0: # Uses R.C.H. Cheng, "The generation of Gamma # variables with non-integral shape parameters", # Applied Statistics, (1977), 26, No. 1, p71-74 # (according to Python's "gammavariate") ainv = sqrt(2.0*alpha - 1.0) beta = 1.0 / ainv bbb = alpha - GeneralRandomStream.__LN4 ccc = alpha + ainv while True: while True: u1 = self.runif01() u2 = self.runif01() v = beta * safelog(safediv(u1, 1.0-u1)) w = alpha * exp(v) c1 = u1 * u1 * u2 r = bbb + ccc*v - w c2 = r + 2.5040773967762742 - 4.5*c1 # 2.5040773967762742 = 1.0 + log(4.5) if c2 >= 0.0 or r >= safelog(c1): break x = w / lam if x <= xmax: break x = kept_within(0.0, x) return x
def rbeta(self, a, b, x1, x2): """ The beta distribution f = x**(a-1) * (1-x)**(b-1) / beta(a, b) The cdf is the integral = the incomplete beta or the incomplete beta/complete beta depending on how the incomplete beta function is defined. x, a, b >= 0; x2 > x1 The algorithm is due to Berman (1970)/Jonck (1964) (for a and b < 1.0) and Cheng (1978) as described in Bratley, Fox and Schrage. """ assert a > 0.0, \ "shape parameters a and b must both be > 0.0 in rbeta!" assert b > 0.0, \ "shape parameters a and b must both be > 0.0 in rbeta!" assert x2 >= x1, \ "support span must not be negative in rbeta!" if x2 == x1: return x1 if a == 1.0 and b == 1.0: y = self.runif01() elif is_posinteger(a) and is_posinteger(b): nstop = int(a + b - 1.0) r = [] for k in range(0, nstop): r.append(self.runif01()) r.sort() y = r[int(a-1.0)] elif a < 1.0 and b < 1.0: a1 = 1.0 / a b1 = 1.0 / b while True: u = pow(self.runif01(), a1) v = pow(self.runif01(), b1) if u + v <= 1.0: y = u / (u+v) break else: alpha = a + b if min(a, b) <= 1.0: beta = 1.0 / min(a, b) else: beta = sqrt((alpha-2.0)/(2.0*a*b-alpha)) gamma = a + 1.0/beta while True: u1 = self.runif01() u2 = self.runif01() u1 = kept_within(TINY, u1, ONEMMACHEPS) u2 = kept_within(TINY, u2, HUGE) comp1 = safelog(u1*u1*u2) v = beta * safelog(safediv(u1, 1.0-u1)) w = a * exp(v) comp2 = alpha*safelog(alpha/(b+w)) + gamma*v - \ GeneralRandomStream.__LN4 if comp2 >= comp1: y = w / (b+w) break x = y*(x2-x1) + x1 x = kept_within(x1, x, x2) return x