def binfreq(n, integer=True): """ Computes all binomial coefficients for n AND the relative frequencies AND the cumulative frequencies and returns them in two lists. Uses fbincoeff (but floats are returned). """ assert is_posinteger(n), "argument to binFreq must be a positive integer!" z = 0.0 # A float farray = [] np1 = n + 1 for k in range(0, np1): x = fbincoeff(n, k, integer) z = z + x # A float farray.append(x) parray = [] carray = [] y = 0.0 for k in range(0, np1): x = farray[k] / z y = y + x parray.append(x) carray.append(y) return parray, carray
def _checkinput(self, cll, t, tnext, tolf, tola, maxitn, imprv): """ Used to check the values of the input parameters to the solver methods. cll is the name of the present method (a string). """ assert tnext > t, "time step must be positive in " + cll + "!" wtext1 = "tolerances smaller than machine epsilon are not recommended " wtext2 = "in " + cll + ". Machine epsilon is used instead" wtext = wtext1 + wtext2 if tolf < MACHEPS: tolf = MACHEPS warn(wtext) if tola < MACHEPS: tola = MACHEPS warn(wtext) assert is_posinteger(maxitn), \ "maxitn must be a positive integer in " + cll + "!" if not is_nonneginteger(imprv): imprv = 0 wtext1 = "imprv must be a non-negative integer in " wtext2 = cll + "! imprv=0 is used instead" wtext = wtext1 + wtext2 warn(wtext)
def binprob(n, phi, integer=True): """ Computes all binomial terms for n given the Bernoulli probability phi. Returns the relative frequencies AND the cumulative frequencies (two lists). """ assert is_posinteger(n), \ "first argument to binProb must be a positive integer!" assert 0.0 <= phi <= 1.0, \ "Bernoulli probability must be in [0.0, 1.0] in binProb!" farray = [] np1 = n + 1 for k in range(0, np1): x = fbincoeff(n, k, integer) farray.append(x) parray = [] carray = [] y = 0.0 q = 1.0 - phi for k in range(0, np1): x = farray[k] * phi**k * q**(n - k) x = kept_within(0.0, x, 1.0) y = y + x y = kept_within(0.0, y, 1.0) parray.append(x) carray.append(y) return parray, carray
def __init__(self, nseed=2147483647, heir=None): """ Initiates the random stream using the input seed 'nseed' and Python's __init__ constructor method. Unless... ...the input seed 'nseed' happens to be a list or tuple of numbers in [0.0, 1.0], in which case this external feed will be used as the basis of all random variate generation for the instance and will be used in place of consecutively sampled numbers from Python's built-in "random" method! """ if isinstance(nseed, int): assert is_posinteger(nseed), \ "The seed (if not a feed) must be a positive integer in ABCRand!" rstream = Random(nseed) self._feed = False self.runif01 = rstream.random if heir != "InverseRandomStream": self.randrange = rstream.randrange self.randint = rstream.randint self.vonmisesvariate = rstream.vonmisesvariate # Random.paretovariate and Random.weibullvariate # are used by methods in GeneralRandomStream self._paretovariate = rstream.paretovariate self._weibullvariate = rstream.weibullvariate else: # nseed is a list or tuple # Check to see beforehand that no numbers # from the feed is outside [0.0, 1.0] for x in nseed: assert 0.0 <= x <= 1.0, \ "number from feed is outside of [0.0, 1.0] in ABCRand!" self._feed = Stack(nseed) # Creates a Stack object self.runif01 = self.__rfeed01
def cerlang(nshape, phasemean, x): """ The cdf of the Erlang distribution. Represents the sum of nshape exponentially distributed random variables, all having "phasemean" as mean """ if nshape == 1: cdf = cexpo(phasemean, x) else: assert is_posinteger(nshape), "shape parameter must be a positive integer in cerlang!" assert phasemean >= 0.0, "phase mean must not be negative in cerlang!" assert x >= 0.0, "variate must not be negative in cerlang!" y = x / float(phasemean) cdf = 1.0 term = 1.0 cdf = term for k in range(1, nshape): term = term * y / k cdf = cdf + term cdf = 1.0 - exp(-y) * cdf cdf = kept_within(0.0, cdf, 1.0) return cdf
def rbinomial(self, n, phi): """ The binomial distribution: p(N=k) = bincoeff * phi**k * (1-phi)**(n-k); n >= 1; k = 0, 1,...., n where phi is the frequency or "Bernoulli probability". Algorithm taken from ORNL-RSIC-38, Vol II (1973). """ assert is_posinteger(n), \ "n must be a positive integer in rbinomial!" assert 0.0 < phi and phi < 1.0, \ "frequency parameter is out of range in rbinomial!" normconst = 10.0 onemphi = 1.0 - phi if phi < 0.5: w = int(round(normconst * onemphi / phi)) else: w = int(round(normconst * phi / onemphi)) if n > w: #------------------------------------------------------- k = int(round(self.rnormal(n*phi, sqrt(n*phi*onemphi)))) else: #------------------------------------------------------- if phi < 0.25: k = -1 m = 0 phi = - safelog(onemphi) while m < n: r = self.rexpo(1.0) j = 1 + int(r/phi) m += j k += 1 if m == n: k += 1 elif phi > 0.75: k = n + 1 m = 0 phi = - safelog(phi) while m < n: r = self.rexpo(1.0) j = 1 + int(r/phi) m += j k -= 1 if m == n: k -= 1 else: # if 0.25 <= phi and phi <= 0.75: k = 0 m = 0 while m < n: r = self.runif01() if r < phi: k += 1 m += 1 k = kept_within(0, k, n) return k
def binprob(n, phi, integer=True): """ Computes all binomial terms for n given the Bernoulli probability phi. Returns the relative frequencies AND the cumulative frequencies (two lists). """ assert is_posinteger(n), \ "first argument to binProb must be a positive integer!" assert 0.0 <= phi <= 1.0, \ "Bernoulli probability must be in [0.0, 1.0] in binProb!" farray = [] np1 = n + 1 for k in range(0, np1): x = fbincoeff(n, k, integer) farray.append(x) parray = [] carray = [] y = 0.0 q = 1.0 - phi for k in range(0, np1): x = farray[k] * phi**k * q**(n-k) x = kept_within(0.0, x, 1.0) y = y + x y = kept_within(0.0, y, 1.0) parray.append(x) carray.append(y) return parray, carray
def derlang(nshape, phasemean, x): """ The pdf of the Erlang distribution - represents the sum of nshape exponentially distributed random variables all having the same mean = phasemean. """ if nshape == 1: pdf = dexpo(phasemean, x) else: # Input check ----------------- assert is_posinteger(nshape), \ "shape parameter must be a positive integer in derlang!" assert phasemean > 0.0, \ "phase mean must be positive in derlang!" assert x >= 0.0, \ "variate must not be negative in derlang!" # ----------------------------- y = x / float(phasemean) nshapem1 = nshape - 1 try: pdf = exp(-y) * y**(nshape-1) except OverflowError: pdf = exp(-y + nshapem1*log(y)) fact = 1 for k in range(1, nshape): fact = fact*k # nshape assumed to be small factor = 1.0 / (phasemean*fact) # Now floats pdf = factor * pdf # Will always be >= 0.0 return pdf
def ibincoeff(n, k, integer=True): """ Computation of a single binomial coefficient n over k, returning an integer (possibly long). For integer=True integer arithmetics is used throughout and there is no risk of overflow. For integer=False a floating- point gamma function approximation is used and the result converted to integer at the end (if an overflow occurs ERRCODE is returned). """ assert is_posinteger(n), \ "n in n_over_k in ibincoeff must be a positive integer!" assert is_nonneginteger(k), \ "k in n_over_k in ibincoeff must be a non-negative integer!" assert n >= k, "n must be >= k in n_over_k in ibincoeff!" if integer: ibico = _bicolongint(n, k) else: try: lnbico = lngamma(n+1) - lngamma(k+1) - lngamma(n-k+1) ibico = safeint(round(exp(lnbico)), 'ibincoeff') except OverflowError: ibico = ERRCODE return ibico
def ibinomial(prob, n, phi, normconst=10.0): """ The binomial distribution: p(N=k) = bincoeff * phi**k * (1-phi)**(n-k), n >= 1, k = 0, 1,...., n """ # Input check ----------- _assertprob(prob, 'ibinomial') assert is_posinteger(n), "n must be a positive integer in ibinomial!" assert 0.0 <= phi and phi <= 1.0, \ "success frequency out of support range in ibinomial!" assert normconst >= 10.0, \ "parameter limit for normal approx. in ibinomial must not be < 10.0!" # ----------------------- onemphi = 1.0 - phi if phi < 0.5: w = normconst * onemphi / phi else: w = normconst * phi / onemphi if n > w: k = int(round(inormal(prob, n*phi, sqrt(n*phi*onemphi)))) else: k = 0 cdf = binProb(n, phi)[1] while True: if cdf[k] <= prob: k = k + 1 else: break k = kept_within(0, k, n) return k
def ibincoeff(n, k, integer=True): """ Computation of a single binomial coefficient n over k, returning an integer (possibly long). For integer=True integer arithmetics is used throughout and there is no risk of overflow. For integer=False a floating- point gamma function approximation is used and the result converted to integer at the end (if an overflow occurs ERRCODE is returned). """ assert is_posinteger(n), \ "n in n_over_k in ibincoeff must be a positive integer!" assert is_nonneginteger(k), \ "k in n_over_k in ibincoeff must be a non-negative integer!" assert n >= k, "n must be >= k in n_over_k in ibincoeff!" if integer: ibico = _bicolongint(n, k) else: try: lnbico = lngamma(n + 1) - lngamma(k + 1) - lngamma(n - k + 1) ibico = safeint(round(exp(lnbico)), 'ibincoeff') except OverflowError: ibico = ERRCODE return ibico
def fbincoeff(n, k, integer=True): """ Computation of a single binomial coefficient n over k, returning a float (an OverflowError returns float('inf'), which would occur for n > 1029 for IEEE754 floating-point standard). """ assert is_posinteger(n), \ "n in n_over_k in fbincoeff must be a positive integer!" assert is_nonneginteger(k), \ "k in n_over_k in fbincoeff must be a non-negative integer!" assert n >= k, "n must be >= k in n_over_k in fbincoeff!" if integer: bico = _bicolongint(n, k) try: fbico = round(float(bico)) except OverflowError: fbico = float('inf') else: try: lnbico = lngamma(n+1) - lngamma(k+1) - lngamma(n-k+1) fbico = round(exp(lnbico)) except OverflowError: fbico = float('inf') return fbico
def rerlang(self, nshape, phasemean, xmax=float('inf')): """ Generator of Erlang-distributed random variates. Represents the sum of nshape exponentially distributed random variables, each having the same mean value = phasemean. For nshape = 1 it works as a generator of exponentially distributed random numbers. """ assert is_posinteger(nshape), \ "shape parameter must be a positive integer in rerlang!" assert phasemean >= 0.0, "phasemean must not be negative in rerlang!" assert xmax >= 0.0, "variate max must be non-negative in rerlang!" if nshape < GeneralRandomStream.__ERLANG2GAMMA: while True: x = 1.0 for k in range(0, nshape): x *= self.runif01() # Might turn out to be zero... x = - phasemean * safelog(x) if x <= xmax: break else: # Gamma is OK while True: x = phasemean * self.rgamma(float(nshape), 1.0) if x <= xmax: break x = kept_within(0.0, x) return x
def fbincoeff(n, k, integer=True): """ Computation of a single binomial coefficient n over k, returning a float (an OverflowError returns float('inf'), which would occur for n > 1029 for IEEE754 floating-point standard). """ assert is_posinteger(n), \ "n in n_over_k in fbincoeff must be a positive integer!" assert is_nonneginteger(k), \ "k in n_over_k in fbincoeff must be a non-negative integer!" assert n >= k, "n must be >= k in n_over_k in fbincoeff!" if integer: bico = _bicolongint(n, k) try: fbico = round(float(bico)) except OverflowError: fbico = float('inf') else: try: lnbico = lngamma(n + 1) - lngamma(k + 1) - lngamma(n - k + 1) fbico = round(exp(lnbico)) except OverflowError: fbico = float('inf') return fbico
def derlang(nshape, phasemean, x): """ The pdf of the Erlang distribution - represents the sum of nshape exponentially distributed random variables all having the same mean = phasemean. """ if nshape == 1: pdf = dexpo(phasemean, x) else: # Input check ----------------- assert is_posinteger(nshape), \ "shape parameter must be a positive integer in derlang!" assert phasemean > 0.0, \ "phase mean must be positive in derlang!" assert x >= 0.0, \ "variate must not be negative in derlang!" # ----------------------------- y = x / float(phasemean) nshapem1 = nshape - 1 try: pdf = exp(-y) * y**(nshape - 1) except OverflowError: pdf = exp(-y + nshapem1 * log(y)) fact = 1 for k in range(1, nshape): fact = fact * k # nshape assumed to be small factor = 1.0 / (phasemean * fact) # Now floats pdf = factor * pdf # Will always be >= 0.0 return pdf
def rerlang(self, nshape, phasemean, xmax=float('inf')): """ Generator of Erlang-distributed random variates. Represents the sum of nshape exponentially distributed random variables, each having the same mean value = phasemean. For nshape = 1 it works as a generator of exponentially distributed random numbers. """ assert is_posinteger(nshape), \ "shape parameter must be a positive integer in rerlang!" assert phasemean >= 0.0, "phasemean must not be negative in rerlang!" assert xmax >= 0.0, "variate max must be non-negative in rerlang!" if nshape < GeneralRandomStream.__ERLANG2GAMMA: while True: x = 1.0 for k in range(0, nshape): x *= self.runif01() # Might turn out to be zero... x = -phasemean * safelog(x) if x <= xmax: break else: # Gamma is OK while True: x = phasemean * self.rgamma(float(nshape), 1.0) if x <= xmax: break x = kept_within(0.0, x) return x
def cerlang(nshape, phasemean, x): """ The cdf of the Erlang distribution. Represents the sum of nshape exponentially distributed random variables, all having "phasemean" as mean """ if nshape == 1: cdf = cexpo(phasemean, x) else: assert is_posinteger(nshape), \ "shape parameter must be a positive integer in cerlang!" assert phasemean >= 0.0, \ "phase mean must not be negative in cerlang!" assert x >= 0.0, \ "variate must not be negative in cerlang!" y = x / float(phasemean) cdf = 1.0 term = 1.0 cdf = term for k in range(1, nshape): term = term * y / k cdf = cdf + term cdf = 1.0 - exp(-y) * cdf cdf = kept_within(0.0, cdf, 1.0) return cdf
def rbinomial(self, n, phi): """ The binomial distribution: p(N=k) = bincoeff * phi**k * (1-phi)**(n-k); n >= 1; k = 0, 1,...., n where phi is the frequency or "Bernoulli probability". Algorithm taken from ORNL-RSIC-38, Vol II (1973). """ assert is_posinteger(n), \ "n must be a positive integer in rbinomial!" assert 0.0 < phi and phi < 1.0, \ "frequency parameter is out of range in rbinomial!" normconst = 10.0 onemphi = 1.0 - phi if phi < 0.5: w = int(round(normconst * onemphi / phi)) else: w = int(round(normconst * phi / onemphi)) if n > w: #------------------------------------------------------- k = int(round(self.rnormal(n * phi, sqrt(n * phi * onemphi)))) else: #------------------------------------------------------- if phi < 0.25: k = -1 m = 0 phi = -safelog(onemphi) while m < n: r = self.rexpo(1.0) j = 1 + int(r / phi) m += j k += 1 if m == n: k += 1 elif phi > 0.75: k = n + 1 m = 0 phi = -safelog(phi) while m < n: r = self.rexpo(1.0) j = 1 + int(r / phi) m += j k -= 1 if m == n: k -= 1 else: # if 0.25 <= phi and phi <= 0.75: k = 0 m = 0 while m < n: r = self.runif01() if r < phi: k += 1 m += 1 k = kept_within(0, k, n) return k
def runif_int0N(self, number): """ Generator of uniformly distributed integers in [0, number) (also the basis of some other procedures for generating random variates). Numbers returned are 0 through number-1. NB!!!!!!! """ assert is_posinteger(number) return int(number*self.runif01())
def runif_int0N(self, number): """ Generator of uniformly distributed integers in [0, number) (also the basis of some other procedures for generating random variates). Numbers returned are 0 through number-1. NB!!!!!!! """ assert is_posinteger(number) return int(number * self.runif01())
def __init__(self, nseed=2147483647): """ Initiates the object and sets the seed. """ errtxt = "The seed must be a positive integer in GeneralRandomStream\n" errtxt += "\t(external feeds cannot be used)" assert is_posinteger(nseed), errtxt ABCRand.__init__(self, nseed) self._feed = False
def __init__(self, nseed=2147483647): """ The seed 'nseed' must be a positive integer or a feed (a list or a tuple) of numbers in [0.0, 1.0]! """ if isinstance(nseed, int): errtxt = "The seed (if not a feed) must be a positive\n" errtxt += "\tinteger in InverseRandomStream!" assert is_posinteger(nseed), errtxt ABCRand.__init__(self, nseed, 'InverseRandomStream')
def __init__(self, nseed=2147483647): """ Initiates the instance object and sets cumulative quantities to zero. """ errtxt = "The seed must be a positive integer in CumulRandomStream\n" errtxt += "\t(external feeds cannot be used)" assert is_posinteger(nseed), errtxt rstream = Random(nseed) self.runif01 = rstream.random self.__tcum = 0.0 # For all except rpieceexpo_cum and rinhomexpo_cum self.__cumul = 0.0 # For rpieceexpo_cum self.__ticum = 0.0 # For rinhomexpo_cum
def rpoisson(self, lam, tspan, nmax=False, pmax=1.0): """ The Poisson distribution: p(N=n) = exp(-lam*tspan) * (lam*tspan)**n / n! n = 0, 1, ...., infinity A maximum number for the output may be given in nmax - then it must be a positive integer. """ pmx = pmax if is_posinteger(nmax): pmx = cpoisson(lam, tspan, nmax) p = pmx * self.runif01() n = ipoisson(p, lam, tspan) return n
def cgeometric(phi, k): """ The geometric distribution with p(K=k) = phi * (1-phi)**(k-1) and P(K>=k) = sum phi * (1-phi)**k = 1 - q**k where q = 1 - phi and 0 < phi <= 1 is the success frequency or "Bernoulli probability" and K >= 1 is the number of trials to the first success in a series of Bernoulli trials. It is easy to prove that P(k) = 1 - (1-phi)**k: let q = 1 - phi. p(k) = (1-q) * q**(k-1) = q**(k-1) - q**k. Then P(1) = p(1) = 1 - q. P(2) = p(1) + p(2) = 1 - q + q - q**2 = 1 - q**2. Induction can be used to show that P(k) = 1 - q**k = 1 - (1-phi)**k """ assert 0.0 < phi and phi <= 1.0, "success frequency must be in (0.0, 1.0] in cgeometric!" assert is_posinteger(k), "number of trials must be a positive integer in cgeometric!" cdf = 1.0 - (1.0 - phi) ** k return cdf
def lusubs_imp(matrix, lower, upper, bvector, permlist, xvector, \ tolf=SQRTMACHEPS, nitermax=4): """ May be used to polish the result from lusubs (some of the necessary checks are made in lusubs). tolf is the maximum fractional difference between two consecutive sums of absolute values of the output vector, and nitermax is the maximum number of improvements carried out regardless of whether the tolerance is met or not. """ assert tolf >= 0.0, \ "max fractional tolerance must not be negative in lusubs_imp!" assert is_posinteger(nitermax), \ "max number of iterations must be a positive number in lusubs_imp!" ndim = len(bvector) sumx = fsum(abs(x) for x in xvector) converged = False for n in range(0, nitermax): resid = array('d', []) # will get len = ndim for k in range(0, ndim): sdp = -bvector[k] for j in range(0, ndim): sdp += matrix[k][j] * xvector[j] resid.append(sdp) resid = lusubs(lower, upper, resid, permlist) for k in range(0, ndim): xvector[k] = xvector[k] - resid[k] sumn = fsum(abs(x) for x in xvector) if abs(sumn - sumx) < sumn * tolf: converged = True break sumx = sumn wtext = "lusubs_imp did not converge. Try changing tolerance or nitermax" if not converged: warn(wtext) return xvector
def cgeometric(phi, k): """ The geometric distribution with p(K=k) = phi * (1-phi)**(k-1) and P(K>=k) = sum phi * (1-phi)**k = 1 - q**k where q = 1 - phi and 0 < phi <= 1 is the success frequency or "Bernoulli probability" and K >= 1 is the number of trials to the first success in a series of Bernoulli trials. It is easy to prove that P(k) = 1 - (1-phi)**k: let q = 1 - phi. p(k) = (1-q) * q**(k-1) = q**(k-1) - q**k. Then P(1) = p(1) = 1 - q. P(2) = p(1) + p(2) = 1 - q + q - q**2 = 1 - q**2. Induction can be used to show that P(k) = 1 - q**k = 1 - (1-phi)**k """ assert 0.0 < phi and phi <= 1.0, \ "success frequency must be in (0.0, 1.0] in cgeometric!" assert is_posinteger(k), \ "number of trials must be a positive integer in cgeometric!" cdf = 1.0 - (1.0 - phi)**k return cdf
def lnbincoeff(n, k, integer=True): """ Computation of the natural logarithm of a single binomial coefficient n over k """ assert is_posinteger(n), \ "n in n_over_k in lnbincoeff must be a positive integer!" assert is_nonneginteger(k), \ "k in n_over_k in lnbincoeff must be a non-negative integer!" assert n >= k, "n must be >= k in n_over_k in lnbincoeff!" if integer: bico = _bicolongint(n, k) lnbico = log(bico) else: lnbico = lngamma(n+1) - lngamma(k+1) - lngamma(n-k+1) return lnbico
def lnbincoeff(n, k, integer=True): """ Computation of the natural logarithm of a single binomial coefficient n over k """ assert is_posinteger(n), \ "n in n_over_k in lnbincoeff must be a positive integer!" assert is_nonneginteger(k), \ "k in n_over_k in lnbincoeff must be a non-negative integer!" assert n >= k, "n must be >= k in n_over_k in lnbincoeff!" if integer: bico = _bicolongint(n, k) lnbico = log(bico) else: lnbico = lngamma(n + 1) - lngamma(k + 1) - lngamma(n - k + 1) return lnbico
def lusubs_imp(matrix, lower, upper, bvector, permlist, xvector, \ tolf=SQRTMACHEPS, nitermax=4): """ May be used to polish the result from lusubs (some of the necessary checks are made in lusubs). tolf is the maximum fractional difference between two consecutive sums of absolute values of the output vector, and nitermax is the maximum number of improvements carried out regardless of whether the tolerance is met or not. """ assert tolf >= 0.0, \ "max fractional tolerance must not be negative in lusubs_imp!" assert is_posinteger(nitermax), \ "max number of iterations must be a positive number in lusubs_imp!" ndim = len(bvector) sumx = fsum(abs(x) for x in xvector) converged = False for n in range(0, nitermax): resid = array('d', []) # will get len = ndim for k in range(0, ndim): sdp = -bvector[k] for j in range(0, ndim): sdp += matrix[k][j]*xvector[j] resid.append(sdp) resid = lusubs(lower, upper, resid, permlist) for k in range(0, ndim): xvector[k] = xvector[k] - resid[k] sumn = fsum(abs(x) for x in xvector) if abs(sumn-sumx) < sumn*tolf: converged = True break sumx = sumn wtext = "lusubs_imp did not converge. Try changing tolerance or nitermax" if not converged: warn(wtext) return xvector
def sercorr(vector, nlag=1): """ sercorr returns the serial correlation coefficient for the sequence of values in the input vector (list/'d' array/tuple) nlag steps apart. """ assert is_posinteger(nlag), \ "Lag ('nlag') must be a positive integer in sercorr!" nlagp2 = nlag + 2 assert len(vector) >= nlagp2, "There must be at least " + str(nlagp2) + \ " elements in input sequence to sercorr!" vector1 = list(vector) vector2 = list(vector) for k in range(0, nlag): del vector1[-1] del vector2[0] amean1, amean2, var1, var2, cov12, rho12 = covar(vector1, vector2) return rho12
def bracketzero(func, x1, x2, caller='caller', factor=GOLDPHI1, maxniter=32): # GOLDPHI1 is approx. 1.6 """ Bracket a root by expanding from the input "guesses" x1, x2. NB. It is not required that x2 > x1. Designed for use prior to any of the one-variable equation solvers. The function carries out a maximum of 'maxniter' iterations, each one expanding the original span by a factor of 'factor', until a span is reached in which there is a zero crossing. """ assert factor > 1.0, "Expansion factor must be > 1.0 in bracketzero!" assert is_posinteger(maxniter), \ "Maximum number of iterations must be a positive integer in bracketzero!" lo = min(x1, x2) up = max(x1, x2) flo = func(lo) fup = func(up) for k in range(0, maxniter): if fsign(flo) != fsign(fup): return lo, up if abs(flo) < abs(fup): lo += factor * (lo - up) flo = func(lo) else: up += factor * (up - lo) fup = func(up) errtxt1 = "Root bracketing failed after " + str(maxniter) errtxt2 = " iterations in bracketzero " + "(called from " + caller + ")" raise Error(errtxt1 + errtxt2)
def rpoisson(self, lam, tspan, nmax=False): """ The Poisson distribution: p(N=n) = exp(-lam*tspan) * (lam*tspan)**n / n! n = 0, 1, ...., infinity A maximum number for the output may be given in nmax - then it must be a positive integer. """ assert lam >= 0.0, "Poisson rate must not be negative in rpoisson!" assert tspan >= 0.0, "time span must not be negative in rpoisson!" if is_posinteger(nmax): nmaxflag = True else: nmaxflag = False lamtau = lam*tspan if lamtau < 64.0: while True: p = self.runif01() n = 0 r = exp(-lamtau) c = r f = float(lamtau) while c <= p: n = n + 1 r *= f/n c += r if not nmaxflag: break if nmaxflag and n<=nmax: break n = max(0, n) return n else: while True: p = self.runif01() n = ipoisson(p, lam, tspan) # Faster than rej'n vs. the Cauchy if not nmaxflag: break if nmaxflag and n<=nmax: break
def rpoisson(self, lam, tspan, nmax=False): """ The Poisson distribution: p(N=n) = exp(-lam*tspan) * (lam*tspan)**n / n! n = 0, 1, ...., infinity A maximum number for the output may be given in nmax - then it must be a positive integer. """ assert lam >= 0.0, "Poisson rate must not be negative in rpoisson!" assert tspan >= 0.0, "time span must not be negative in rpoisson!" if is_posinteger(nmax): nmaxflag = True else: nmaxflag = False lamtau = lam * tspan if lamtau < 64.0: while True: p = self.runif01() n = 0 r = exp(-lamtau) c = r f = float(lamtau) while c <= p: n = n + 1 r *= f / n c += r if not nmaxflag: break if nmaxflag and n <= nmax: break n = max(0, n) return n else: while True: p = self.runif01() n = ipoisson(p, lam, tspan) # Faster than rej'n vs. the Cauchy if not nmaxflag: break if nmaxflag and n <= nmax: break
def sercorrnormvector(self, n, rho, mu=0.0, sigma=1.0): """ Generates a list of serially correlated normal random variates. Requires that the rstream object is punched out from the GeneralRandomStream class! """ # Input check ----- assert is_posinteger(n) # sigma and rho are checked in rstream.rnormal and rstream.rcorr_normal # ----------------- vector = [] rstream = self.rstream x = rstream.rnormal(mu, sigma) vector.append(x) for k in range(1, n): x = rstream.rcorr_normal(rho, mu, sigma, mu, sigma, x) vector.append(x) return vector
def cerlang_gen(nshapes, pcumul, phasemean, x): """ The generalized Erlang distribution - the Erlang equivalent of the hyperexpo distribution f = sumk pk * ferlang(m, nk), F = sumk pk * Ferlang(m, nk), the same mean for all phases. NB Input to the function is the list of CUMULATIVE PROBABILITIES ! """ ln = len(nshapes) lp = len(pcumul) # Input check ----------------- assert x >= 0.0, "variate must not be negative in cerlang_gen!" assert lp == ln, "number of shapes must be equal to the number of pcumuls in cerlang_gen!" if ln == 1: return derlang(nshapes[0], phasemean, x) errortextn = "all nshapes must be positive integers i cerlang_gen!" errortextm = "the mean must be a positive float in cerlang_gen!" errortextp = "pcumul list is not in order in cerlang_gen!" for n in nshapes: assert is_posinteger(n), errortextn assert phasemean > 0.0, errortextm assert pcumul[-1] == 1.0, errortextp assert 0.0 < pcumul[0] and pcumul[0] <= 1.0, errortextp # ----------------------------- summ = pcumul[0] * cerlang(nshapes[0], phasemean, x) nvalues = lp for k in range(1, nvalues): pdiff = pcumul[k] - pcumul[k - 1] assert pdiff >= 0.0, errortextp summ += pdiff * cerlang(nshapes[k], phasemean, x) cdf = summ cdf = kept_within(0.0, cdf, 1.0) return cdf
def derlang_gen(nshapes, pcumul, phasemean, x): """ The pdf of the generalized Erlang distribution - the Erlang equivalent of the hyperexpo distribution: f = sumk pk * ferlang(m, nk) F = sumk pk * Ferlang(m, nk) with the same mean for all phases. NB Input to the function is the list of CUMULATIVE PROBABILITIES ! """ ln = len(nshapes) lp = len(pcumul) # Input check ----------------- assert x >= 0.0, "variate must not be negative in derlang_gen!" assert lp == ln, \ "number of shapes must be equal to the number of pcumuls in derlang_gen!" if ln == 1: return cerlang(nshapes[0], mean, x) errortextn = "all nshapes must be positiva heltal i derlang_gen!" errortextm = "the mean must be a positive float in derlang_gen!" errortextp = "pcumul list is not in order in derlang_gen!" for n in nshapes: assert is_posinteger(n), errortextn assert phasemean > 0.0, errortextm assert pcumul[-1] == 1.0, errortextp assert 0.0 < pcumul[0] and pcumul[0] <= 1.0, errortextp # ----------------------------- summ = pcumul[0] * derlang(nshapes[0], phasemean, x) nvalues = lp for k in range(1, nvalues): pdiff = pcumul[k] - pcumul[k - 1] assert pdiff >= 0.0, errortextp summ += pdiff * derlang(nshapes[k], phasemean, x) pdf = summ return pdf
def bracketzero(func, x1, x2, caller='caller', factor=GOLDPHI1, maxniter=32): # GOLDPHI1 is approx. 1.6 """ Bracket a root by expanding from the input "guesses" x1, x2. NB. It is not required that x2 > x1. Designed for use prior to any of the one-variable equation solvers. The function carries out a maximum of 'maxniter' iterations, each one expanding the original span by a factor of 'factor', until a span is reached in which there is a zero crossing. """ assert factor > 1.0, "Expansion factor must be > 1.0 in bracketzero!" assert is_posinteger(maxniter), \ "Maximum number of iterations must be a positive integer in bracketzero!" lo = min(x1, x2) up = max(x1, x2) flo = func(lo) fup = func(up) for k in range(0, maxniter): if fsign(flo) != fsign(fup): return lo, up if abs(flo) < abs(fup): lo += factor*(lo-up) flo = func(lo) else: up += factor*(up-lo) fup = func(up) errtxt1 = "Root bracketing failed after " + str(maxniter) errtxt2 = " iterations in bracketzero " + "(called from " + caller + ")" raise Error(errtxt1 + errtxt2)
def cgamma(alpha, lam, x, lngamalpha=False, tolf=FOURMACHEPS, itmax=128): """ The gamma distrib. f = lam * exp(-lam*x) * (lam*x)**(alpha-1) / gamma(alpha) F is the integral = the incomplete gamma or the incomplete gamma / complete gamma depending on how the incomplete gamma function is defined. x, lam, alpha >= 0 tolf = allowed fractional error in computation of the incomplete function itmax = maximum number of iterations to obtain accuracy NB It is possible to gain efficiency by providing the value of the natural logarithm of the complete gamma function ln(gamma(alpha)) as a pre-computed input (may be computed using numlib.specfunc.lngamma) instead of the default 'False'. """ assert alpha >= 0.0, "alpha must not be negative in cgamma!" assert lam >= 0.0, "lambda must not be negative i cgamma!" assert x >= 0.0, "variate must not be negative in cgamma!" assert tolf >= 0.0, "tolerance must not be negative in cgamma!" assert is_posinteger(itmax), "maximum number of iterations must be a positive integer in cgamma!" if alpha == 1.0: return cexpo(1.0 / lam, x) lamx = lam * x if lamx == 0.0: return 0.0 if lngamalpha: lnga = lngamalpha else: lnga = lngamma(alpha) # ------------------------------------------------------------------------- def _gamser(): # A series expansion is used for lamx < alpha + 1.0 # (cf. Abramowitz & Stegun) apn = alpha summ = 1.0 / apn dela = summ converged = False for k in range(0, itmax): apn += 1.0 dela = dela * lamx / apn summ += dela if abs(dela) < abs(summ) * tolf: converged = True return summ * exp(-lamx + alpha * log(lamx) - lnga), converged return summ * exp(-lamx + alpha * log(lamx) - lnga), converged # ------------------------------------------------------------------------- def _gamcf(): # A continued fraction expansion is used for # lamx >= alpha + 1.0 (cf. Abramowitz & Stegun): gold = 0.0 a0 = 1.0 a1 = lamx b0 = 0.0 b1 = 1.0 fac = 1.0 converged = False for k in range(0, itmax): ak = float(k + 1) aka = ak - alpha a0 = (a1 + a0 * aka) * fac b0 = (b1 + b0 * aka) * fac akf = ak * fac a1 = lamx * a0 + akf * a1 b1 = lamx * b0 + akf * b1 if a1 != 0.0: fac = 1.0 / a1 g = b1 * fac if abs(g - gold) < abs(g) * tolf: converged = True return 1.0 - exp(-lamx + alpha * log(lamx) - lnga) * g, converged gold = g return 1.0 - exp(-lamx + alpha * log(lamx) - lnga) * g, converged # ------------------------------------------------------------------------- if lamx < alpha + 1.0: cdf, converged = _gamser() else: cdf, converged = _gamcf() if not converged: warn("cgamma has not converged for itmax = " + str(itmax) + " and tolf = " + str(tolf)) cdf = kept_within(0.0, cdf, 1.0) return cdf
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 zbrent(func, x1, x2, caller='caller', tolf=FOURMACHEPS, \ tola=SQRTTINY, maxniter=128, bracket=False): """ Solves the equation func(x) = 0 on [x1, x2] using a variant of Richard Brent's algorithm (more like the "ZEROIN" of Forsythe-Malcolm-Moler). NB. The function always returns a value but a warning is printed to stdout if the iteration procedure has not converged! Cf. comment below regarding convergence! Arguments: ---------- func Function having the proposed root as its argument x1 Lower search limit (root must be known to be >= x1 unless prior bracketing is used) x2 Upper search limit (root must be known to be <= x2 unless prior bracketing is used) tolf Desired fractional accuracy of root (a combination of fractional and absolute will actually be used: tolf*abs(root) + tola). tolf should not be < 4.0*machine epsilon since this may inhibit convergence! tola Desired absolute accuracy of root (a combination of fractional and absolute will actually be used: tolf*abs(root) + tola) maxniter Maximum number of iterations bracket If True, x1 and x2 are used in an initial bracketing before solving Returns: --------- Final value of root This algorithm is claimed to guarantee convergence within about (log2((b-a)/tol))**2 function evaluations, which is more demanding than bisection. For instance: b-a = 1.0 and tol = 1.8e-12 is guaranteed to converge with about 1,500 evaluations. It normally converges with fewer ITERATIONS, however, and for reasonably "smooth and well-behaved" functions it will be on the average more efficient and accurate than bisection. For details on the algorithm see Forsythe-Malcolm-Moler, as well as Brent, R.P.; "An algorithm with guaranteed convergence for finding a zero of a function", The Computer Journal 14(4), pp. 422-425, 1971. """ if tolf < FOURMACHEPS: tolf = FOURMACHEPS wtxt1 = "Fractional tol. less than 4.0*machine epsilon may prevent " wtxt2 = "convergence in zbrent. 4.0*macheps will be used instead!" warn(wtxt1+wtxt2) if tola < 0.0: tola = 0.0 wtxt1 = "Negative absolute tolerance is not a good idea " wtxt2 = "in zbrent. 0.0 (zero) will be used instead!" warn(wtxt1+wtxt2) assert is_posinteger(maxniter), \ "Maximum number of iterations must be a positive integer in zbrent!" if bracket: x1, x2 = bracketzero(func, x1, x2, caller, maxniter) assert x2 > x1, "Bounds must be given with the lower bound first in zbrent!" a = x1 b = x2 c = x2 ############################### NOT IN REFERENCES !!!!! fa = func(x1) if fa == 0.0: return x1 fb = func(x2) if fb == 0.0: return x2 if fsign(fa) == fsign(fb): x1, x2 = bracketzero(func, x1, x2, caller, maxniter) wtxt1 = "Starting points must be on opposite sides of the root in " wtxt2 = "zbrent. Bracketing will be used to find an appropriate span!" warn(wtxt1+wtxt2) fc = fb niter = 0 while niter <= maxniter: niter += 1 if fsign(fb) == fsign(fc): c = a fc = fa d = b - a e = d if abs(fc) < abs(fb): a = b b = c c = a fa = fb fb = fc fc = fa tol = tolf*abs(b) + tola tol1 = 0.5 * tol xm = 0.5 * (c-b) if abs(xm) <= tol1 or fb == 0.0: return b if abs(e) >= tol1 and abs(fa) > abs(fb): s = fb / fa if a == c: p = 2.0 * xm * s q = 1.0 - s else: q = fa / fc r = fb / fc p = s * (2.0*xm*q*(q-r)-(b-a)*(r-1.0)) q = (q-1.0) * (r-1.0) * (s-1.0) if p > 0.0: q = -q p = abs(p) if 2.0*p < min(3.0*xm*q-abs(tol1*q), abs(e*q)): e = d d = p / q else: d = xm e = d else: d = xm e = d a = b fa = fb if abs(d) > tol1: b = b + d else: #b = b + sign(tol1, xm) if xm < 0.0: b = b - tol1 elif xm > 0.0: b = b + tol1 else: b = b fb = func(b) else: numb = int(math.log((x2-x1)/tol, 2)**2 + 0.5) wtxt1 = str(maxniter) + " iterations not sufficient in zbrent called by" wtxt2 = " " + caller + ". func(x) = " + str(fb) + " for x = " + str(b) warn(wtxt1+wtxt2) return b
def zbisect(func, x1, x2, caller='caller', tolf=FOURMACHEPS, \ tola=SQRTTINY, maxniter=256, bracket=False): """ Solves the equation func(x) = 0 on [x1, x2] using a bisection algorithm. zbisect converges slower than zbrent in most cases, but it might be faster in some cases! NB. The function always returns a value but a warning is printed to stdout if the iteration procedure has not converged! Cf. comment below regarding convergence! Arguments: ---------- func Function having the proposed root as its argument x1 Lower search limit (root must be known to be >= x1 unless prior bracketing is used) x2 Upper search limit (root must be known to be <= x2 unless prior bracketing is used) tolf Desired fractional accuracy of root (a combination of fractional and absolute will actually be used: tolf*abs(root) + tola) tola Desired absolute accuracy of root (a combination of fractional and absolute will actually be used: tolf*abs(root) + tola) AND desired max absolute difference of func(root) from zero maxniter Maximum number of iterations bracket If True, x1 and x2 are used in an initial bracketing before solving Returns: --------- Final value of root This algorithm needs on the average log2((b-a)/tol) function evaluations to reach convergence. For instance: b-a = 1.0 and tol = 1.8e-12 will on the average provide convergence in about 40 iterations. Bisection is "dead certain" and will always converge if there is a root. It is likely to pass the tolerances with no extra margin. If there is no root, it will converge to a singularity if there is one... """ if tolf < MACHEPS: tolf = MACHEPS wtxt1 = "Fractional tolerance less than machine epsilon is not a " wtxt2 = "good idea in zbisect. Machine epsilon will be used instead!" warn(wtxt1 + wtxt2) if tola < 0.0: tola = 0.0 wtxt1 = "Negative absolute tolerance is not a good idea " wtxt2 = "in zbisect. 0.0 (zero) will be used instead!" warn(wtxt1 + wtxt2) assert is_posinteger(maxniter), \ "Maximum number of iterations must be a positive integer in zbisect!" if bracket: x1, x2 = bracketzero(func, x1, x2, caller, maxniter) assert x2 > x1, \ "Bounds must be given with the lower bound first in zbisect!" fmid = func(x2) if fmid == 0.0: return x2 f = func(x1) if f == 0.0: return x1 if fsign(fmid) == fsign(f): x1, x2 = bracketzero(func, x1, x2, caller, maxniter) wtxt1 = "Starting points must be on opposite sides of the root in " wtxt2 = "zbisect. Bracketing will be used to find an appropriate span!" warn(wtxt1 + wtxt2) if f < 0.0: root = x1 h = x2 - x1 else: root = x2 h = x1 - x2 niter = 0 while niter <= maxniter: niter += 1 h = 0.5 * h xmid = root + h fmid = func(xmid) if abs(fmid) < tola: return xmid if fmid <= 0.0: root = xmid absh = abs(h) if absh < tolf * abs(root) + tola: return root else: wtxt1 = str(maxniter) + " it'ns not sufficient in zbisect called by " wtxt2 = caller + ".\nfunc(x) = " + str(fmid) + " for x = " + str(root) warn(wtxt1 + wtxt2) return root
def zbrent(func, x1, x2, caller='caller', tolf=FOURMACHEPS, \ tola=SQRTTINY, maxniter=128, bracket=False): """ Solves the equation func(x) = 0 on [x1, x2] using a variant of Richard Brent's algorithm (more like the "ZEROIN" of Forsythe-Malcolm-Moler). NB. The function always returns a value but a warning is printed to stdout if the iteration procedure has not converged! Cf. comment below regarding convergence! Arguments: ---------- func Function having the proposed root as its argument x1 Lower search limit (root must be known to be >= x1 unless prior bracketing is used) x2 Upper search limit (root must be known to be <= x2 unless prior bracketing is used) tolf Desired fractional accuracy of root (a combination of fractional and absolute will actually be used: tolf*abs(root) + tola). tolf should not be < 4.0*machine epsilon since this may inhibit convergence! tola Desired absolute accuracy of root (a combination of fractional and absolute will actually be used: tolf*abs(root) + tola) maxniter Maximum number of iterations bracket If True, x1 and x2 are used in an initial bracketing before solving Returns: --------- Final value of root This algorithm is claimed to guarantee convergence within about (log2((b-a)/tol))**2 function evaluations, which is more demanding than bisection. For instance: b-a = 1.0 and tol = 1.8e-12 is guaranteed to converge with about 1,500 evaluations. It normally converges with fewer ITERATIONS, however, and for reasonably "smooth and well-behaved" functions it will be on the average more efficient and accurate than bisection. For details on the algorithm see Forsythe-Malcolm-Moler, as well as Brent, R.P.; "An algorithm with guaranteed convergence for finding a zero of a function", The Computer Journal 14(4), pp. 422-425, 1971. """ if tolf < FOURMACHEPS: tolf = FOURMACHEPS wtxt1 = "Fractional tol. less than 4.0*machine epsilon may prevent " wtxt2 = "convergence in zbrent. 4.0*macheps will be used instead!" warn(wtxt1 + wtxt2) if tola < 0.0: tola = 0.0 wtxt1 = "Negative absolute tolerance is not a good idea " wtxt2 = "in zbrent. 0.0 (zero) will be used instead!" warn(wtxt1 + wtxt2) assert is_posinteger(maxniter), \ "Maximum number of iterations must be a positive integer in zbrent!" if bracket: x1, x2 = bracketzero(func, x1, x2, caller, maxniter) assert x2 > x1, "Bounds must be given with the lower bound first in zbrent!" a = x1 b = x2 c = x2 ############################### NOT IN REFERENCES !!!!! fa = func(x1) if fa == 0.0: return x1 fb = func(x2) if fb == 0.0: return x2 if fsign(fa) == fsign(fb): x1, x2 = bracketzero(func, x1, x2, caller, maxniter) wtxt1 = "Starting points must be on opposite sides of the root in " wtxt2 = "zbrent. Bracketing will be used to find an appropriate span!" warn(wtxt1 + wtxt2) fc = fb niter = 0 while niter <= maxniter: niter += 1 if fsign(fb) == fsign(fc): c = a fc = fa d = b - a e = d if abs(fc) < abs(fb): a = b b = c c = a fa = fb fb = fc fc = fa tol = tolf * abs(b) + tola tol1 = 0.5 * tol xm = 0.5 * (c - b) if abs(xm) <= tol1 or fb == 0.0: return b if abs(e) >= tol1 and abs(fa) > abs(fb): s = fb / fa if a == c: p = 2.0 * xm * s q = 1.0 - s else: q = fa / fc r = fb / fc p = s * (2.0 * xm * q * (q - r) - (b - a) * (r - 1.0)) q = (q - 1.0) * (r - 1.0) * (s - 1.0) if p > 0.0: q = -q p = abs(p) if 2.0 * p < min(3.0 * xm * q - abs(tol1 * q), abs(e * q)): e = d d = p / q else: d = xm e = d else: d = xm e = d a = b fa = fb if abs(d) > tol1: b = b + d else: #b = b + sign(tol1, xm) if xm < 0.0: b = b - tol1 elif xm > 0.0: b = b + tol1 else: b = b fb = func(b) else: numb = int(math.log((x2 - x1) / tol, 2)**2 + 0.5) wtxt1 = str( maxniter) + " iterations not sufficient in zbrent called by" wtxt2 = " " + caller + ". func(x) = " + str(fb) + " for x = " + str(b) warn(wtxt1 + wtxt2) return b
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 cgamma(alpha, lam, x, lngamalpha=False, tolf=FOURMACHEPS, itmax=128): """ The gamma distrib. f = lam * exp(-lam*x) * (lam*x)**(alpha-1) / gamma(alpha) F is the integral = the incomplete gamma or the incomplete gamma / complete gamma depending on how the incomplete gamma function is defined. x, lam, alpha >= 0 tolf = allowed fractional error in computation of the incomplete function itmax = maximum number of iterations to obtain accuracy NB It is possible to gain efficiency by providing the value of the natural logarithm of the complete gamma function ln(gamma(alpha)) as a pre-computed input (may be computed using numlib.specfunc.lngamma) instead of the default 'False'. """ assert alpha >= 0.0, "alpha must not be negative in cgamma!" assert lam >= 0.0, "lambda must not be negative i cgamma!" assert x >= 0.0, "variate must not be negative in cgamma!" assert tolf >= 0.0, "tolerance must not be negative in cgamma!" assert is_posinteger(itmax), \ "maximum number of iterations must be a positive integer in cgamma!" if alpha == 1.0: return cexpo(1.0 / lam, x) lamx = lam * x if lamx == 0.0: return 0.0 if lngamalpha: lnga = lngamalpha else: lnga = lngamma(alpha) # ------------------------------------------------------------------------- def _gamser(): # A series expansion is used for lamx < alpha + 1.0 # (cf. Abramowitz & Stegun) apn = alpha summ = 1.0 / apn dela = summ converged = False for k in range(0, itmax): apn += 1.0 dela = dela * lamx / apn summ += dela if abs(dela) < abs(summ) * tolf: converged = True return summ * exp(-lamx + alpha * log(lamx) - lnga), converged return summ * exp(-lamx + alpha * log(lamx) - lnga), converged # ------------------------------------------------------------------------- def _gamcf(): # A continued fraction expansion is used for # lamx >= alpha + 1.0 (cf. Abramowitz & Stegun): gold = 0.0 a0 = 1.0 a1 = lamx b0 = 0.0 b1 = 1.0 fac = 1.0 converged = False for k in range(0, itmax): ak = float(k + 1) aka = ak - alpha a0 = (a1 + a0 * aka) * fac b0 = (b1 + b0 * aka) * fac akf = ak * fac a1 = lamx * a0 + akf * a1 b1 = lamx * b0 + akf * b1 if a1 != 0.0: fac = 1.0 / a1 g = b1 * fac if abs(g - gold) < abs(g) * tolf: converged = True return 1.0 - exp(-lamx + alpha * log(lamx) - lnga) * g, converged gold = g return 1.0 - exp(-lamx + alpha * log(lamx) - lnga) * g, converged # ------------------------------------------------------------------------- if lamx < alpha + 1.0: cdf, converged = _gamser() else: cdf, converged = _gamcf() if not converged: warn("cgamma has not converged for itmax = " + \ str(itmax) + " and tolf = " + str(tolf)) cdf = kept_within(0.0, cdf, 1.0) return cdf
def talbot(ftilde, tim, sigma, nu=1.0, tau=3.0, ntrap=32): """ Function: computes the value of the inverse Laplace transform of a given complex function for a given time using Talbot's algorithm. It calculates the sum in formula (31) on p. 104 in A. Talbot: J Inst Maths Applics 23(1979), 97-120. Besides making sure that the special Talbotian integration contour is to the right of all poles (the algorithm works best when all poles are located on the real axis...), the user also has to make sure that the contour does not coincide with other singularities. Inputs: Name Description ------ ----------- ftilde name of function to be inverted (must be a separate complex function having one complex argument) tim time for which the inversion is to be carried out (float; must be > 0.0) sigma shift parameter of the talbot integration contour; determined by the position of the rightmost pole of ftilde: its value (float) must be located to the right of all singularities but should be as close to the rightmost singularity as possible (imprves accuracy and helps reducing the number of steps needed for the integration) nu shape parameter (float; > 0.0) tau scale parameter (float; > 0.0) ntrap number of points in trapezoidal integration (integer; > 0) (actually: one more will be used for the first term/point). Try using fewer than the default - a good choice of sigma will help! Outputs: Name Description ------ ----------- fnval value of inverse (float) """ # -------------------------------- assert tim > 0.0, "time must be a positive float in talbot!" assert nu > 0.0, "the shape parameter must be a positive float in talbot!" assert tau > 0.0, "the scale parameter must be a positive float in talbot!" assert is_posinteger(ntrap), \ "the number of steps must be a positive integer in talbot!" # Compute scaled inverted time lam = tau / tim # Initiate lists for subsequent summing (positive and negative # terms are summed separately to prevent cancellation) termn = [] termp = [] # Compute the first term of the series thetak = 0.0 term = 0.5 * _refthetak(ftilde, sigma, nu, tau, lam, thetak) if term < 0.0: termn.append(term) elif term > 0.0: termp.append(term) #else: no need to add zeros # Compute the rest of the terms aux = PI / ntrap for k in range(1, ntrap): thetak = k * aux term = _refthetak(ftilde, sigma, nu, tau, lam, thetak) if term < 0.0: termn.append(term) elif term > 0.0: termp.append(term) #else: no need to add zeros # Sort the positive terms in ascending order and sum them up termp.sort() sump = sum(trm for trm in termp) # Sort the negative terms in descending order and sum them up termn.sort() termn.reverse() sumn = sum(trm for trm in termn) # Combine sums and multiply by constant factor to obtain final sum/value summ = sump + sumn ratio = lam / ntrap fnval = summ * exp(sigma*tim) * ratio return fnval
def zbisect(func, x1, x2, caller='caller', tolf=FOURMACHEPS, \ tola=SQRTTINY, maxniter=256, bracket=False): """ Solves the equation func(x) = 0 on [x1, x2] using a bisection algorithm. zbisect converges slower than zbrent in most cases, but it might be faster in some cases! NB. The function always returns a value but a warning is printed to stdout if the iteration procedure has not converged! Cf. comment below regarding convergence! Arguments: ---------- func Function having the proposed root as its argument x1 Lower search limit (root must be known to be >= x1 unless prior bracketing is used) x2 Upper search limit (root must be known to be <= x2 unless prior bracketing is used) tolf Desired fractional accuracy of root (a combination of fractional and absolute will actually be used: tolf*abs(root) + tola) tola Desired absolute accuracy of root (a combination of fractional and absolute will actually be used: tolf*abs(root) + tola) AND desired max absolute difference of func(root) from zero maxniter Maximum number of iterations bracket If True, x1 and x2 are used in an initial bracketing before solving Returns: --------- Final value of root This algorithm needs on the average log2((b-a)/tol) function evaluations to reach convergence. For instance: b-a = 1.0 and tol = 1.8e-12 will on the average provide convergence in about 40 iterations. Bisection is "dead certain" and will always converge if there is a root. It is likely to pass the tolerances with no extra margin. If there is no root, it will converge to a singularity if there is one... """ if tolf < MACHEPS: tolf = MACHEPS wtxt1 = "Fractional tolerance less than machine epsilon is not a " wtxt2 = "good idea in zbisect. Machine epsilon will be used instead!" warn(wtxt1+wtxt2) if tola < 0.0: tola = 0.0 wtxt1 = "Negative absolute tolerance is not a good idea " wtxt2 = "in zbisect. 0.0 (zero) will be used instead!" warn(wtxt1+wtxt2) assert is_posinteger(maxniter), \ "Maximum number of iterations must be a positive integer in zbisect!" if bracket: x1, x2 = bracketzero(func, x1, x2, caller, maxniter) assert x2 > x1, \ "Bounds must be given with the lower bound first in zbisect!" fmid = func(x2) if fmid == 0.0: return x2 f = func(x1) if f == 0.0: return x1 if fsign(fmid) == fsign(f): x1, x2 = bracketzero(func, x1, x2, caller, maxniter) wtxt1 = "Starting points must be on opposite sides of the root in " wtxt2 = "zbisect. Bracketing will be used to find an appropriate span!" warn(wtxt1+wtxt2) if f < 0.0: root = x1 h = x2 - x1 else: root = x2 h = x1 - x2 niter = 0 while niter <= maxniter: niter += 1 h = 0.5 * h xmid = root + h fmid = func(xmid) if abs(fmid) < tola: return xmid if fmid <= 0.0: root = xmid absh = abs(h) if absh < tolf*abs(root) + tola: return root else: wtxt1 = str(maxniter) + " it'ns not sufficient in zbisect called by " wtxt2 = caller + ".\nfunc(x) = " + str(fmid) + " for x = " + str(root) warn(wtxt1+wtxt2) return root