def dextreme_I(type, mu, scale, x): """ The pdf of the extreme value distribution type I (aka the Gumbel distribution or Gumbel distribution type I): F = exp{-exp[-(x-mu)/scale]} (max variant) f = exp[-(x-mu)/scale] * exp{-exp[-(x-mu)/scale]} / scale F = 1 - exp{-exp[+(x-mu)/scale]} (min variant) f = exp[+(x-mu)/scale] * exp{-exp[+(x-mu)/scale]} / scale type must be 'max' or 'min' scale must be > 0.0 """ assert scale > 0.0, "scale must be positive in dextreme_I!" if type == 'max': fscale = float(scale) h = exp(-(x - mu) / fscale) g = exp(-h) pdf = h * g / fscale elif type == 'min': fscale = float(scale) h = exp((x - mu) / fscale) g = exp(-h) pdf = h * g / fscale else: raise Error("type must be either 'max' or 'min' in cextreme_I!") pdf = kept_within(0.0, pdf) return pdf
def call_next_in_line(self, calltime): """ Fetch the first arrival time at the front end of the line, remove it from the line, and make one server busy. Outputs: -------- The arrival time at the front end of the line """ self.__length[calltime - self.__prevctl] = len(self.__line) self.__idleh[calltime - self.__prevcti] = self.nfreeserv self.__systemh[calltime-self.__prevcts] = len(self.__line) + \ self.__nserv - self.__nfreeserv self.__prevctl = calltime self.__prevcti = calltime self.__prevcts = calltime arrivaltime = self.__line.shift() self.__nfreeserv -= 1 if self.__nfreeserv < 0: raise Error("Number of servers are negative in call_next_in_line!") self.__wtimes.append(calltime - arrivaltime) self.__ninline = len(self.__line) return arrivaltime
def iextreme_I(prob, type='max', mu=0.0, scale=1.0): """ Extreme value distribution type I (aka the Gumbel distribution or Gumbel distribution type I): F = exp{-exp[-(x-mu)/scale]} (max variant) f = exp[-(x-mu)/scale] * exp{-exp[-(x-mu)/scale]} / scale F = 1 - exp{-exp[+(x-mu)/scale]} (min variant) f = exp[+(x-mu)/scale] * exp{-exp[+(x-mu)/scale]} / scale type must be 'max' or 'min' scale must be > 0.0 """ _assertprob(prob, 'iextreme_I') assert scale >= 0.0, "scale parameter must not be negative in iextreme_I!" if type == 'max': x = - scale*safelog(-safelog(prob)) + mu elif type == 'min': x = scale*safelog(-safelog(1.0-prob)) + mu else: raise Error("iextreme_I: type must be either 'max' or 'min'") return x
def dextreme_gen(type, shape, mu, scale, x): """ The pdf of the generalized extreme value distribution: F = exp{-[1-shape*(x-mu)/scale]**(1/shape)} (max version) f = [1-shape*(x-mu)/scale]**(1/shape-1) * exp{-[1-shape*(x-mu)/scale]**(1/shape)} / scale F = 1 - exp{-[1+shape*(x-mu)/scale]**(1/shape)} (min version) f = [1+shape*(x-mu)/scale]**(1/shape-1) * exp{-[1+shape*(x-mu)/scale]**(1/shape)} / scale shape < 0 => Type II shape > 0 => Type III shape -> 0 => Type I - Gumbel type must be 'max' or 'min' scale must be > 0.0 A REASONABLE SCHEME SEEMS TO BE mu = scale WHICH SEEMS TO LIMIT THE DISTRIBUTION TO EITHER SIDE OF THE Y-AXIS! """ if shape == 0.0: pdf = dextreme_I(type, mu, scale, x) else: assert scale > 0.0, "scale must be positive in dextreme_gen!" if type == 'max': fscale = float(scale) epahs = 1.0 / shape crucial = 1.0 - shape * (x - mu) / float(scale) if crucial <= 0.0 and shape < 0.0: pdf = 0.0 else: g = exp(-crucial**epahs) h = crucial**(epahs - 1.0) pdf = h * g / scale elif type == 'min': fscale = float(scale) epahs = 1.0 / shape crucial = 1.0 + shape * (x - mu) / float(scale) if crucial <= 0.0 and shape < 0.0: pdf = 0.0 else: g = exp(-crucial**epahs) h = crucial**(epahs - 1.0) pdf = h * g / scale else: raise Error("type must be either 'max' or 'min' in dextreme_gen!") pdf = kept_within(0.0, pdf) return pdf
def squaredim(matrix, caller='caller'): """ Test for squareness. """ nrows, ncols = sized(matrix, 'squaredim') if ncols != nrows: errortext = "Unsquare matrix in " + caller + "!" raise Error(errortext) else: ndim = ncols return ndim
def xabs(x): """ Replaces the built-in abs. Handles real and complex numbers and scalars as well as lists/tuples, matrix row vectors and matrix column vectors having the nested type of structure defined in the Matrix class. Arguments: ---------- x The argument may be a float or a complex number or a list/tuple, or a nested list/tuple vector structure containing floats or complex numbers Outputs: -------- The absolute value of the input (float) """ try: nrows = len(x) # OK it's a list try: ncols = len(x[0]) # OK it's even a matrix vector! if nrows == 1: # It's a matrix row vector a2 = 0.0 for k in range(0, ncols): z = complex(x[0][k]) a2 = a2 + z.real*z.real + z.imag*z.imag elif ncols == 1: # It's a matrix column vector a2 = 0.0 for k in range(0, nrows): z = complex(x[k][0]) a2 = a2 + z.real*z.real + z.imag*z.imag else: raise Error("abs: argument not a vector nor a scalar") except TypeError: # It was a list but not a matrix vector a2 = 0.0 for k in range(0, nrows): z = complex(x[k]) a2 = a2 + z.real*z.real + z.imag*z.imag except TypeError: # Well, it was merely a scalar! z = complex(x) a2 = z.real*z.real + z.imag*z.imag return sqrt(a2) # end of xabs #-------------------------------------------------------------------------------
def zero(self, nrows, ncols): """ Create a zero matrix with dimension nrows*ncols from scratch. """ if self: raise Error("Instance matrix exists already in Matrix.zero!") for k in range(0, nrows): row = array('d', ncols * [0.0]) self.append(row) self.__nrows = nrows self.__ncols = ncols
def cextreme_gen(type, shape, mu, scale, x): """ Generalized extreme value distribution: F = exp{-[1-shape*(x-mu)/scale]**(1/shape)} (max version) f = [1-shape*(x-mu)/scale]**(1/shape-1) * exp{-[1-shape*(x-mu)/scale]**(1/shape)} / scale F = 1 - exp{-[1+shape*(x-mu)/scale]**(1/shape)} (min version) f = [1+shape*(x-mu)/scale]**(1/shape-1) * exp{-[1+shape*(x-mu)/scale]**(1/shape)} / scale shape < 0 => Type II shape > 0 => Type III shape -> 0 => Type I - Gumbel type must be 'max' or 'min' scale must be > 0.0 A REASONABLE SCHEME SEEMS TO BE mu = scale WHICH SEEMS TO LIMIT THE DISTRIBUTION TO EITHER SIDE OF THE Y-AXIS! """ if shape == 0.0: cdf = cextreme_I(type, mu, scale, x) else: assert scale > 0.0, "scale must be positive in cextreme_gen!" if type == 'max': crucial = 1.0 - shape * (x - mu) / float(scale) if crucial <= 0.0 and shape < 0.0: cdf = 0.0 else: y = crucial**(1.0 / shape) cdf = exp(-y) elif type == 'min': crucial = 1.0 + shape * (x - mu) / float(scale) if crucial <= 0.0 and shape < 0.0: cdf = 1.0 else: y = crucial**(1.0 / shape) cdf = 1.0 - exp(-y) else: raise Error("type must be either 'max' or 'min' in cextreme_gen!") cdf = kept_within(0.0, cdf, 1.0) return cdf
def xmap(func, x): """ Replaces the built-in map and can be used for matrices belonging to the misclib.Matrix class, or lists with the same structure, as well as ordinary lists and tuples. Arguments: ---------- func function defined externally that takes 'x' as its sole non-default argument, x an ordinary list, tuple, array or a Matrix Outputs: --------- The modified list """ if isinstance(x, Matrix): nrows = len(x) ncols = len(x[0]) # It's even a matrix! z = [] for k in range(0, nrows): y = [] for j in range(0, ncols): y.append(func(x[k][j])) y = array('d', y) z.append(y) elif is_darray(x): nrows = len(x) #z = [] z = array('d', z) for k in range(0, nrows): z.append(func(x[k])) #z = array('d', z) elif isinstance(x, (tuple, Stack, list)): nrows = len(x) z = [] for k in range(0, nrows): z.append(func(x[k])) else: errtxt1 = "input state vector must be a tuple, a stack, " errtxt2 = "a list, a 'd' array or a Matrix!" raise Error(errtxt1 + errtxt2) return z
def blank(self, nrows, ncols): """ Create a matrix with dimension nrows*ncols containing only NaN elements from scratch. """ if self: raise Error("Instance matrix exists already in Matrix.blank!") for k in range(0, nrows): row = array('d', ncols * [float('nan')]) self.append(row) self.__nrows = nrows self.__ncols = ncols
def unity(self, ndim): """ Create a unity matrix from scratch. """ if self: raise Error("Instance matrix exists already in Matrix.unity!") for k in range(0, ndim): row = array('d', ndim * [0.0]) row[k] = 1.0 self.append(row) self.__nrows = ndim self.__ncols = ndim
def splice(self, *arg): """ Same as the push method but None is returned and iflag is not used (the method is motivated by the fact that there is a corresponding Stack method). """ raise Error("splice is not implemented in the Heap class!") # end of splice # ------------------------------------------------------------------------------ # end of Heap # ------------------------------------------------------------------------------
def __setattr__(self, attr_name, value): """ This method overrides the built-in __setattr__ and makes the values of the internal attributes externally available more difficult to screw up from the outside. """ if attr_name == "ninline" \ or attr_name == "nfreeserv" \ or attr_name == "narriv" \ or attr_name == "nbalked" \ or attr_name == "renegers" \ or attr_name == "nreneged" \ or attr_name == "nescaped" : errtxt1 = ": Can't change value/length of attribute" errtxt2 = " from the outside!" raise Error(attr_name + errtxt1 + errtxt2) else: self.__dict__[attr_name] = value
def iextreme_gen(prob, type, shape, mu=0.0, scale=1.0): """ Generalized extreme value distribution: F = exp{-[1-shape*(x-mu)/scale]**(1/shape)} (max version) f = [1-shape*(x-mu)/scale]**(1/shape-1) * exp{-[1-shape*(x-mu)/scale]**(1/shape)} / scale F = 1 - exp{-[1+shape*(x-mu)/scale]**(1/shape)} (min version) f = [1+shape*(x-mu)/scale]**(1/shape-1) * exp{-[1+shape*(x-mu)/scale]**(1/shape)} / scale shape < 0 => Type II shape > 0 => Type III shape -> 0 => Type I - Gumbel type must be 'max' or 'min' scale must be > 0.0 A REASONABLE SCHEME SEEMS TO BE mu = scale WHICH SEEMS TO LIMIT THE DISTRIBUTION TO EITHER SIDE OF THE Y-AXIS! """ if shape == 0.0: x = iextreme_I(prob, type, mu, scale) else: _assertprob(prob, 'iextreme_gen') assert scale >= 0.0 if type == 'max': x = scale*(1.0-(-safelog(prob))**shape)/shape + mu elif type == 'min': x = scale*((-safelog(1.0-prob))**shape-1.0)/shape + mu else: raise Error("iextreme_gen: type must be either 'max' or 'min'") return x
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 cextreme_I(type, mu, scale, x): """ Extreme value distribution type I (aka the Gumbel distribution or Gumbel distribution type I): F = exp{-exp[-(x-mu)/scale]} (max variant) f = exp[-(x-mu)/scale] * exp{-exp[-(x-mu)/scale]} / scale F = 1 - exp{-exp[+(x-mu)/scale]} (min variant) f = exp[+(x-mu)/scale] * exp{-exp[+(x-mu)/scale]} / scale type must be 'max' or 'min' scale must be > 0.0 """ assert scale > 0.0, "scale must be positive in cextreme_I!" if type == 'max': cdf = exp(-exp(-(x - mu) / float(scale))) elif type == 'min': cdf = 1.0 - exp(-exp((x - mu) / float(scale))) else: raise Error("type must be either 'max' or 'min' in cextreme_I!") cdf = kept_within(0.0, cdf, 1.0) return cdf
def __init__(self, line, nserv): """ line is 'Line' or 'LineStack' nserv is the initial number of servers """ # Attributes made available from the outside (not assignable, though): # -------------------------------------------------------------------- self.__narriv = 0 # Accumulated number of arrivers until present self.__ninline = 0 # Present number waiting in line self.__nfreeserv = nserv # Present number of free servers self.__nbalked = 0 # Accumulated number of balkers self.__nreneged = 0 # Accumulated number of renegers self.__nescaped = 0 # = self.__nbalked + self.__nreneged # Attributes not available from the outside: # -------------------------------------------------------------------- self.__nserv = nserv # Initial number of free servers if line == 'Line': self.__line = Deque() elif line == 'LineStack': self.__line = Stack() else: raise Error("'line' must be 'Line' or 'LineStack' in ABCLine!") # dict containing the "reneging times" (time self.__renegers = {} # points of the future reneging events) with # the corresponding arrival times as keys self.__wtimes = array('d', []) # 'd' array of waiting times for those not escaped self.__length = {} # dict containing the line length history with # the corresponding time spans as keys self.__prevctl = 0.0 # The previous clock time when the line length # was last changed # dict containing the history of the number of self.__systemh = {} # customers in the system with the corresponding # time spans as keys self.__prevcts = 0.0 # The previous clock time when the number of # customers in system was last changed self.__length = {} # dict containing the line length history with # the corresponding time spans as keys self.__prevctl = 0.0 # The previous clock time when the line length # was last changed # dict containing the history of the number of self.__idleh = {} # of free/idle servers in the system with the # corresponding time spans as keys self.__prevcti = 0.0 # The previous clock time when the number of