def GM1FundamentalMatrix(A, precision=1e-14, maxNumIt=50, method="ShiftPWCR", dual="R", maxNumRoot=2048, shiftType="one"): """ Returns matrix R corresponding to the G/M/1 type Markov chain given by matrices A. Matrix R is the minimal non-negative solution of the following matrix equation: .. math:: R = A_0 + R A_1 + R^2 A_2 + R^3 A_3 + \dots. The implementation is based on [1]_, please cite it if you use this method. Parameters ---------- A : length(M) list of matrices of shape (N,N) Matrix blocks of the G/M/1 type generator in the regular part, from 0 to M-1. precision : double, optional Matrix R is computed iteratively up to this precision. The default value is 1e-14 maxNumIt : int, optional The maximal number of iterations. The default value is 50. method : {"CR", "RR", "NI", "FI", "IS"}, optional The method used to solve the matrix-quadratic equation (CR: cyclic reduction, RR: Ramaswami reduction, NI: Newton iteration, FI: functional iteration, IS: invariant subspace method). The default is "CR". Returns ------- R : matrix, shape (N,N) The R matrix of the G/M/1 type Markov chain. References ---------- .. [1] Bini, D. A., Meini, B., Steffé, S., Van Houdt, B. (2006, October). Structured Markov chains solver: software tools. In Proceeding from the 2006 workshop on Tools for solving structured Markov chains (p. 14). ACM. """ A = np.hstack(A) m = A.shape[0] I = ml.eye(m) dega = A.shape[1] // m - 1 # compute invariant vector of A and the drift # drift > 1: positive recurrent GIM1, drift < 1: transient GIM1 sumA = A[:, dega * m:] beta = np.sum(sumA, 1) # beta = (A_maxd)e + (A_maxd + A_maxd-1)e + ... + (Amaxd+...+A1)e for i in range(dega - 1, 0, -1): sumA = sumA + A[:, i * m:(i + 1) * m] beta = beta + np.sum(sumA, 1) sumA = sumA + A[:, :m] theta = DTMCSolve(sumA) drift = theta * beta if dual == "R" or (dual == "A" and drift <= 1): # RAM dual # compute the RAM Dual process for i in range(dega + 1): A[:, i * m:(i + 1) * m] = Diag(1.0 / theta) * A[:, i * m:(i + 1) * m].T * Diag(theta) else: # Bright dual if drift > 1: # A -> positive recurrent GIM1 # compute the Caudal characteristic of A eta, v = GM1TypeCaudal(A) else: # A -> transient GIM1 (=recurrent MG1) eta, v = MG1TypeDecay(A) # compute invariant vector of A0+A1*eta+A2*eta^2+...+Amax*eta^max sumAeta = eta**dega * A[:, dega * m:] for i in range(dega - 1, -1, -1): sumAeta = sumAeta + eta**i * A[:, i * m:(i + 1) * m] theta = DRPSolve(sumAeta + (1.0 - eta) * I) # compute the Bright Dual process for i in range(dega + 1): A[:, i * m:(i + 1) * m] = eta**(i - 1) * Diag( 1.0 / theta) * A[:, i * m:(i + 1) * m].T * Diag(theta) G = MG1FundamentalMatrix(A, precision, maxNumIt, method, maxNumRoot, shiftType) if dual == "R" or (dual == "A" and drift <= 1): # RAM dual return Diag(1.0 / theta) * G.T * Diag(theta) else: # Bright dual return Diag(1.0 / theta) * G.T * Diag(theta) * eta
def RandomDPH(order, mean=10.0, zeroEntries=0, maxTrials=1000, prec=1e-7): """ Returns a random discrete phase-type distribution with a given mean value. Parameters ---------- order : int The size of the discrete phase-type distribution mean : double, optional The mean of the discrete phase-type distribution zeroEntries : int, optional The number of zero entries in the initial vector, generator matrix and closing vector maxTrials : int, optional The maximum number of trials to find a proper DPH (that has an irreducible phase process and none of its parameters is all-zero). The default value is 1000. prec : double, optional Numerical precision for checking the irreducibility. The default value is 1e-14. Returns ------- alpha : vector, shape (1,M) The initial probability vector of the phase-type distribution. A : matrix, shape (M,M) The transient generator matrix of the phase-type distribution. Notes ----- If the procedure fails, try to increase the 'maxTrials' parameter, or increase the mean value. """ if zeroEntries > (order + 1) * (order - 1): raise Exception( "RandomDPH: Too many zero entries requested! Try to decrease the zero entries number!" ) # distribute the zero entries among the rows def allZeroDistr(states, zeros): if states == 1: return [[zeros]] else: o = [] for i in range(zeros + 1): x = allZeroDistr(states - 1, zeros - i) for j in range(len(x)): xt = x[j] xt.append(i) xt.sort() # check if we have it already if o.count(xt) == 0: o.append(xt) return o zeroDistr = allZeroDistr(order, zeroEntries) trials = 1 while trials < maxTrials: # select a configuration from zeroDistr: it is a list describing the zero entries in each row zdix = np.random.permutation(len(zeroDistr)) for k in range(len(zeroDistr)): zDistr = zeroDistr[zdix[k]] B = np.zeros((order, order + 2)) for i in range(order): rp = np.random.permutation(order + 1) a = np.zeros(order + 1) for j in range(order + 1 - zDistr[i]): a[rp[j]] = np.random.rand() B[i, 0:i] = a[0:i] B[i, i + 1:] = a[i:] # construct DPH parameters A = ml.matrix(B[:, :order]) a = ml.matrix(B[:, order + 1]).T sc = np.sum(A, 1) + a.A if np.any(sc == 0): continue A = Diag(1 / sc) * A a = Diag(1 / sc) * a alpha = ml.matrix(B[:, order]) # check if it is a proper PH (irreducible phase process & no full zero matrix) if np.all(A == 0.0) or np.all(alpha == 0.0) or np.all(a == 0.0): continue alpha = alpha / np.sum(alpha) if la.matrix_rank(ml.eye(order) - A) == order: if np.min(np.abs(alpha * la.inv(ml.eye(order) - A))) > prec: # diagonals of matrix A: d = np.random.rand(order) # scale to the mean value m = MomentsFromDPH(alpha, Diag(1 - d) * A + Diag(d), 1)[0] d = 1 - (1 - d) * m / mean A = Diag(1 - d) * A + Diag(d) if CheckDPHRepresentation(alpha, A, prec): return (alpha, A) trials += 1 raise Exception("No feasible random PH found with such many zero entries!")
def MAPMAP1(D0, D1, S0, S1, *argv): """ Returns various performane measures of a continuous time MAP/MAP/1 queue. In a MAP/MAP/1 queue both the arrival and the service processes are characterized by Markovian arrival processes. Parameters ---------- D0 : matrix, shape(N,N) The transitions of the arrival MAP not accompanied by job arrivals D1 : matrix, shape(N,N) The transitions of the arrival MAP accompanied by job arrivals S0 : matrix, shape(N,N) The transitions of the service MAP not accompanied by job service S1 : matrix, shape(N,N) The transitions of the service MAP accompanied by job service further parameters : The rest of the function parameters specify the options and the performance measures to be computed. The supported performance measures and options in this function are: +----------------+--------------------+----------------------------------------+ | Parameter name | Input parameters | Output | +================+====================+========================================+ | "ncMoms" | Number of moments | The moments of the number of customers | +----------------+--------------------+----------------------------------------+ | "ncDistr" | Upper limit K | The distribution of the number of | | | | customers from level 0 to level K-1 | +----------------+--------------------+----------------------------------------+ | "ncDistrMG" | None | The vector-matrix parameters of the | | | | matrix-geometric distribution of the | | | | number of customers in the system | +----------------+--------------------+----------------------------------------+ | "ncDistrDPH" | None | The vector-matrix parameters of the | | | | matrix-geometric distribution of the | | | | number of customers in the system, | | | | converted to a discrete PH | | | | representation | +----------------+--------------------+----------------------------------------+ | "stMoms" | Number of moments | The sojourn time moments | +----------------+--------------------+----------------------------------------+ | "stDistr" | A vector of points | The sojourn time distribution at the | | | | requested points (cummulative, cdf) | +----------------+--------------------+----------------------------------------+ | "stDistrME" | None | The vector-matrix parameters of the | | | | matrix-exponentially distributed | | | | sojourn time distribution | +----------------+--------------------+----------------------------------------+ | "stDistrPH" | None | The vector-matrix parameters of the | | | | matrix-exponentially distributed | | | | sojourn time distribution, converted | | | | to a continuous PH representation | +----------------+--------------------+----------------------------------------+ | "prec" | The precision | Numerical precision used as a stopping | | | | condition when solving the | | | | matrix-quadratic equation | +----------------+--------------------+----------------------------------------+ (The quantities related to the number of customers in the system include the customer in the server, and the sojourn time related quantities include the service times as well) Returns ------- Ret : list of the performance measures Each entry of the list corresponds to a performance measure requested. If there is just a single item, then it is not put into a list. Notes ----- "ncDistrMG" and "stDistrME" behave much better numerically than "ncDistrDPH" and "stDistrPH". """ # parse options prec = 1e-14 needST = False eaten = [] for i in range(len(argv)): if argv[i] == "prec": prec = argv[i + 1] eaten.append(i) eaten.append(i + 1) elif type(argv[i]) is str and len( argv[i]) > 2 and argv[i][0:2] == "st": needST = True if butools.checkInput and not CheckMAPRepresentation(D0, D1): raise Exception( 'MAPMAP1: The arrival process (D0,D1) is not a valid MAP representation!' ) if butools.checkInput and not CheckMAPRepresentation(S0, S1): raise Exception( 'MAPMAP1: The service process (S0,S1) is not a valid MAP representation!' ) IA = ml.eye(D0.shape[0]) IS = ml.eye(S0.shape[0]) B = np.kron(IA, S1) L = np.kron(D0, IS) + np.kron(IA, S0) F = np.kron(D1, IS) L0 = np.kron(D0, IS) pi0, R = QBDSolve(B, L, F, L0, prec) N = pi0.shape[1] I = ml.eye(N) if needST: # calculate the distribution of the age at departures U = L + R * B Rh = -U.I * F T = np.kron(IA, S0) + Rh * B eta = pi0 * F * (I - Rh).I eta = eta / np.sum(eta) Ret = [] argIx = 0 while argIx < len(argv): if argIx in eaten: argIx += 1 continue elif type(argv[argIx]) is str and argv[argIx] == "ncDistrDPH": # transform it to DPH alpha = pi0 * R * (I - R).I A = Diag(alpha).I * R.T * Diag(alpha) Ret.append(alpha) Ret.append(A) elif type(argv[argIx]) is str and argv[argIx] == "ncDistrMG": # transform it to MG B = SimilarityMatrixForVectors(np.sum((I - R).I * R, 1), np.ones((N, 1))) Bi = B.I A = B * R * Bi alpha = pi0 * Bi Ret.append(alpha) Ret.append(A) elif type(argv[argIx]) is str and argv[argIx] == "ncMoms": numOfMoms = argv[argIx + 1] argIx += 1 moms = [] iR = (I - R).I for m in range(1, numOfMoms + 1): moms.append( math.factorial(m) * np.sum(pi0 * iR**(m + 1) * R**m)) Ret.append(MomsFromFactorialMoms(moms)) elif type(argv[argIx]) is str and argv[argIx] == "ncDistr": numOfQLProbs = argv[argIx + 1] argIx += 1 values = np.empty(numOfQLProbs) values[0] = np.sum(pi0) RPow = I for p in range(numOfQLProbs - 1): RPow = RPow * R values[p + 1] = np.sum(pi0 * RPow) Ret.append(values) elif type(argv[argIx]) is str and argv[argIx] == "stDistrPH": # transform it to PH representation beta = CTMCSolve(S0 + S1) theta = DTMCSolve(-D0.I * D1) vv = np.kron(theta, beta) ix = np.arange(N) nz = ix[vv.flat > prec] delta = Diag(vv[:, nz]) alpha = ml.ones((1, N)) * B[nz, :].T * delta / np.sum(beta * S1) A = delta.I * T[nz, :][:, nz].T * delta Ret.append(alpha) Ret.append(A) elif type(argv[argIx]) is str and argv[argIx] == "stDistrME": Ret.append(eta) Ret.append(T) elif type(argv[argIx]) is str and argv[argIx] == "stMoms": numOfMoms = argv[argIx + 1] argIx += 1 moms = [] iT = -T.I for m in range(1, numOfMoms + 1): moms.append(math.factorial(m) * np.sum(eta * iT**m)) Ret.append(moms) elif type(argv[argIx]) is str and argv[argIx] == "stDistr": points = argv[argIx + 1] argIx += 1 values = np.empty(points.shape) for p in range(len(points.flat)): values.flat[p] = 1.0 - np.sum( eta * la.expm(T * points.flat[p])) Ret.append(values) else: raise Exception("MAPMAP1: Unknown parameter " + str(argv[argIx])) argIx += 1 if len(Ret) == 1: return Ret[0] else: return Ret
def RandomDMMAP(order, types, mean=10.0, zeroEntries=0, maxTrials=1000, prec=1e-7): """ Returns a random discrete Markovian arrival process. Parameters ---------- order : int The size of the DMAP mean : double, optional The mean inter-arrival times of the DMMAP types : int The number of different arrival types zeroEntries : int, optional The number of zero entries in the D0 and D1 matrices maxTrials : int, optional The maximum number of trials to find a proper DMMAP (that has an irreducible phase process and none of its parameters is all-zero) prec : double, optional Numerical precision for checking the irreducibility. The default value is 1e-14. Returns ------- D : list/cell of matrices of shape(M,M), length(types+1) The D0...Dtypes matrices of the DMMAP Notes ----- If it fails, try to increase the 'maxTrials' parameter, or/and the 'mean' parameter. """ # distribute the zero entries among the rows def allZeroDistr(states, zeros): if states == 1: return [[zeros]] else: o = [] for i in range(zeros + 1): x = allZeroDistr(states - 1, zeros - i) for j in range(len(x)): xt = x[j] xt.append(i) xt.sort() # check if we have it already if o.count(xt) == 0: o.append(xt) return o if zeroEntries > (types + 1) * order * order - 2 * order: raise Exception( "RandomDMAP/DMMAP: Too many zero entries requested! Try to decrease the zeroEntries parameter!" ) zeroDistr = allZeroDistr(order, zeroEntries) trials = 1 while trials < maxTrials: # select a configuration from zeroDistr: it is a list describing the zero entries in each row zdix = np.random.permutation(len(zeroDistr)) for k in range(len(zeroDistr)): zDistr = zeroDistr[zdix[k]] bad = False for d in zDistr: if d >= (types + 1) * order - 1: bad = True break if bad: continue B = np.zeros((order, (types + 1) * order)) for i in range(order): rp = np.random.permutation((types + 1) * order - 1) a = np.zeros((types + 1) * order - 1) for j in range((types + 1) * order - 1 - zDistr[i]): a[rp[j]] = np.random.rand() B[i, 0:i] = a[0:i] B[i, i + 1:] = a[i:] # construct DMMAP matrices D = [] sc = np.zeros(order) for i in range(types + 1): Di = ml.matrix(B[:, i * order:(i + 1) * order]) D.append(Di) sc += np.sum(Di, 1).A.flatten() if np.any(sc == 0): continue for i in range(types + 1): D[i] = Diag(1.0 / sc) * D[i] # check if it is a proper DMAP (irreducible phase process & no full zero matrix) sumD = SumMatrixList(D) if la.matrix_rank(D[0]) == order and la.matrix_rank( ml.eye(D[0].shape[0]) - sumD) == order - 1: alpha = DTMCSolve(sumD) if np.min(np.abs(alpha)) > prec: fullZero = False for Di in D: if np.all(Di == 0.0): fullZero = True break if not fullZero: # diagonals of matrix A: d = np.random.rand(order) # scale to the mean value Dv = [] for i in range(types + 1): Dv.append(Diag(1 - d) * D[i]) Dv[0] = Dv[0] + Diag(d) try: m = MarginalMomentsFromDMMAP(Dv, 1)[0] d = 1 - (1 - d) * m / mean for i in range(types + 1): D[i] = Diag(1 - d) * D[i] D[0] = D[0] + Diag(d) if CheckDMMAPRepresentation(D): return D except: pass trials += 1 raise Exception( "No feasible random DMAP/DMMAP found with such many zero entries! Try to increase the maxTrials parameter!" )
def QBDQueue(B, L, F, L0, *argv): """ Returns various performane measures of a continuous time QBD queue. QBD queues have a background continuous time Markov chain with generator Q whose the transitions can be partitioned into three sets: transitions accompanied by an arrival of a new job (F, forward), transitions accompanied by the service of the current job in the server (B, backward) and internal transitions (L, local). Thus we have Q=B+L+F. L0 is the matrix of local transition rates if the queue is empty. Parameters ---------- B : matrix, shape(N,N) Transitions of the background process accompanied by the service of the current job in the server L : matrix, shape(N,N) Internal transitions of the background process that do not generate neither arrival nor service F : matrix, shape(N,N) Transitions of the background process accompanied by an arrival of a new job L0 : matrix, shape(N,N) Internal transitions of the background process when there are no jobs in the queue further parameters : The rest of the function parameters specify the options and the performance measures to be computed. The supported performance measures and options in this function are: +----------------+--------------------+----------------------------------------+ | Parameter name | Input parameters | Output | +================+====================+========================================+ | "ncMoms" | Number of moments | The moments of the number of customers | +----------------+--------------------+----------------------------------------+ | "ncDistr" | Upper limit K | The distribution of the number of | | | | customers from level 0 to level K-1 | +----------------+--------------------+----------------------------------------+ | "ncDistrMG" | None | The vector-matrix parameters of the | | | | matrix-geometric distribution of the | | | | number of customers in the system | +----------------+--------------------+----------------------------------------+ | "ncDistrDPH" | None | The vector-matrix parameters of the | | | | matrix-geometric distribution of the | | | | number of customers in the system, | | | | converted to a discrete PH | | | | representation | +----------------+--------------------+----------------------------------------+ | "stMoms" | Number of moments | The sojourn time moments | +----------------+--------------------+----------------------------------------+ | "stDistr" | A vector of points | The sojourn time distribution at the | | | | requested points (cummulative, cdf) | +----------------+--------------------+----------------------------------------+ | "stDistrME" | None | The vector-matrix parameters of the | | | | matrix-exponentially distributed | | | | sojourn time distribution | +----------------+--------------------+----------------------------------------+ | "stDistrPH" | None | The vector-matrix parameters of the | | | | matrix-exponentially distributed | | | | sojourn time distribution, converted | | | | to a continuous PH representation | +----------------+--------------------+----------------------------------------+ | "prec" | The precision | Numerical precision used as a stopping | | | | condition when solving the | | | | matrix-quadratic equation | +----------------+--------------------+----------------------------------------+ (The quantities related to the number of customers in the system include the customer in the server, and the sojourn time related quantities include the service times as well) Returns ------- Ret : list of the performance measures Each entry of the list corresponds to a performance measure requested. If there is just a single item, then it is not put into a list. Notes ----- "ncDistrMG" and "stDistrMG" behave much better numerically than "ncDistrDPH" and "stDistrPH". """ # parse options prec = 1e-14 needST = False eaten = [] for i in range(len(argv)): if argv[i] == "prec": prec = argv[i + 1] eaten.append(i) eaten.append(i + 1) elif type(argv[i]) is str and len( argv[i]) > 2 and argv[i][0:2] == "st": needST = True if butools.checkInput and not CheckGenerator(B + L + F): raise Exception( 'QBDQueue: The matrix sum (B+L+F) is not a valid generator of a Markov chain!' ) if butools.checkInput and not CheckGenerator(L0 + F): raise Exception( 'QBDQueue: The matrix sum (L0+F) is not a valid generator of a Markov chain!' ) pi0, R = QBDSolve(B, L, F, L0, prec) N = pi0.shape[1] I = ml.eye(N) if needST: U = L + R * B Rh = (-U).I * F eta = pi0 * F * (I - Rh).I eta = eta / np.sum(eta) z = np.reshape(I, (N * N, 1), 'F') Ret = [] argIx = 0 while argIx < len(argv): if argIx in eaten: argIx += 1 continue elif type(argv[argIx]) is str and argv[argIx] == "ncDistrDPH": # transform it to DPH alpha = pi0 * R * (I - R).I A = Diag(alpha).I * R.T * Diag(alpha) Ret.append(alpha) Ret.append(A) elif type(argv[argIx]) is str and argv[argIx] == "ncDistrMG": # transform it to MG B = SimilarityMatrixForVectors(np.sum((I - R).I * R, 1), np.ones((N, 1))) Bi = B.I A = B * R * Bi alpha = pi0 * Bi Ret.append(alpha) Ret.append(A) elif type(argv[argIx]) is str and argv[argIx] == "ncMoms": numOfMoms = argv[argIx + 1] argIx += 1 moms = [] iR = (I - R).I for m in range(1, numOfMoms + 1): moms.append( math.factorial(m) * np.sum(pi0 * iR**(m + 1) * R**m)) Ret.append(MomsFromFactorialMoms(moms)) elif type(argv[argIx]) is str and argv[argIx] == "ncDistr": numOfQLProbs = argv[argIx + 1] argIx += 1 values = np.empty(numOfQLProbs) values[0] = np.sum(pi0) RPow = I for p in range(numOfQLProbs - 1): RPow = RPow * R values[p + 1] = np.sum(pi0 * RPow) Ret.append(values) elif type(argv[argIx]) is str and argv[argIx] == "stDistrPH": # transform to ph distribution ix = np.arange(N) nz = ix[eta.flat > prec] Delta = Diag(eta) A = np.kron(L + F, I[nz, :][:, nz]) + np.kron( B, Delta[nz, :][:, nz].I * Rh[nz, :][:, nz].T * Delta[nz, :][:, nz]) alpha = z.T * np.kron(I, Delta[:, nz]) Ret.append(alpha) Ret.append(A) elif type(argv[argIx]) is str and argv[argIx] == "stDistrME": # transform it such that the closing vector is a vector of ones # this is the way butools accepts ME distributions Bm = SimilarityMatrixForVectors(z, np.ones(z.shape)) Bmi = Bm.I A = Bm * (np.kron(L.T + F.T, I) + np.kron(B.T, Rh)) * Bmi alpha = np.kron(ml.ones((1, N)), eta) * Bmi Ret.append(alpha) Ret.append(A) elif type(argv[argIx]) is str and argv[argIx] == "stMoms": numOfMoms = argv[argIx + 1] argIx += 1 moms = [] Z = np.kron(L.T + F.T, I) + np.kron(B.T, Rh) iZ = -Z.I for m in range(1, numOfMoms + 1): moms.append( math.factorial(m) * np.sum( np.kron(ml.ones( (1, N)), eta) * iZ**(m + 1) * (-Z) * z)) Ret.append(moms) elif type(argv[argIx]) is str and argv[argIx] == "stDistr": points = argv[argIx + 1] argIx += 1 values = np.empty(points.shape) Z = np.kron(L.T + F.T, I) + np.kron(B.T, Rh) for p in range(len(points.flat)): values.flat[p] = 1.0 - np.sum( np.kron(ml.ones( (1, N)), eta) * la.expm(Z * points.flat[p]) * z) Ret.append(values) else: raise Exception("QBDQueue: Unknown parameter " + str(argv[argIx])) argIx += 1 if len(Ret) == 1: return Ret[0] else: return Ret
def FluFluQueue(Qin, Rin, Qout, Rout, srv0stop, *argv): """ Returns various performane measures of a fluid queue with independent fluid arrival and service processes. Two types of boundary behavior is available. If srv0stop=false, the output process evolves continuously even if the queue is empty. If srv0stop=true, the output process slows down if there is fewer fluid in the queue than it can serve. If the queue is empty and the fluid input rate is zero, the output process freezes till fluid arrives. Parameters ---------- Qin : matrix, shape (N,N) The generator of the background Markov chain corresponding to the input process Rin : matrix, shape (N,N) Diagonal matrix containing the fluid input rates associated to the states of the input background process Qout : matrix, shape (N,N) The generator of the background Markov chain corresponding to the output process Rout : matrix, shape (N,N) Diagonal matrix containing the fluid output rates associated to the states of the input background process srv0stop : bool If true, the service output process slows down if there is fewer fluid in the queue than it can serve. If false, the output process evolves continuously. further parameters : The rest of the function parameters specify the options and the performance measures to be computed. The supported performance measures and options in this function are: +----------------+--------------------+--------------------------------------+ | Parameter name | Input parameters | Output | +================+====================+======================================+ | "flMoms" | Number of moments | The moments of the fluid level | +----------------+--------------------+--------------------------------------+ | "flDistr" | A vector of points | The fluid level distribution at | | | | the requested points (cdf) | +----------------+--------------------+--------------------------------------+ | "flDistrME" | None | The vector-matrix parameters of the | | | | matrix-exponentially distributed | | | | fluid level distribution | +----------------+--------------------+--------------------------------------+ | "flDistrPH" | None | The vector-matrix parameters of the | | | | matrix-exponentially distributed | | | | fluid level distribution, converted | | | | to a PH representation | +----------------+--------------------+--------------------------------------+ | "stMoms" | Number of moments | The sojourn time moments of fluid | | | | drops | +----------------+--------------------+--------------------------------------+ | "stDistr" | A vector of points | The sojourn time distribution at the | | | | requested points (cummulative, cdf) | +----------------+--------------------+--------------------------------------+ | "stDistrME" | None | The vector-matrix parameters of the | | | | matrix-exponentially distributed | | | | sojourn time distribution | +----------------+--------------------+--------------------------------------+ | "stDistrPH" | None | The vector-matrix parameters of the | | | | matrix-exponentially distributed | | | | sojourn time distribution, converted | | | | to a PH representation | +----------------+--------------------+--------------------------------------+ | "prec" | The precision | Numerical precision to check if the | | | | input is valid and it is also used | | | | as a stopping condition when solving | | | | the Riccati equation | +----------------+--------------------+--------------------------------------+ Returns ------- Ret : list of the performance measures Each entry of the list corresponds to a performance measure requested. If there is just a single item, then it is not put into a list. Notes ----- "flDistrME" and "stDistrME" behave much better numerically than "flDistrPH" and "stDistrPH". References ---------- .. [1] Horvath G, Telek M, "Sojourn times in fluid queues with independent and dependent input and output processes PERFORMANCE EVALUATION 79: pp. 160-181, 2014. """ # parse options prec = 1e-14 needST = False needQL = False Q0 = [] eaten = [] for i in range(len(argv)): if type(argv[i]) is str and argv[i]=="prec": prec = argv[i+1] eaten.append(i) eaten.append(i+1) elif type(argv[i]) is str and len(argv[i])>2 and argv[i][0:2]=="st": needST = True elif type(argv[i]) is str and len(argv[i])>2 and argv[i][0:2]=="fl": needQL = True if butools.checkInput and not CheckGenerator(Qin,False): raise Exception('FluFluQueue: Generator matrix Qin is not Markovian!') if butools.checkInput and not CheckGenerator(Qout,False): raise Exception('FluFluQueue: Generator matrix Qout is not Markovian!') if butools.checkInput and (np.any(np.diag(Rin)<-butools.checkPrecision) or np.any(np.diag(Rout)<-butools.checkPrecision)): raise Exception('FluFluQueue: Fluid rates Rin and Rout must be non-negative !') Iin = ml.eye(Qin.shape[0]) Iout = ml.eye(Qout.shape[0]) if needQL: Q = np.kron(Qin,Iout)+np.kron(Iin,Qout) if srv0stop: Q0 = np.kron(Qin,Iout)+np.kron(Rin, la.pinv(Rout)*Qout) else: Q0 = Q mass0, ini, K, clo = GeneralFluidSolve (Q, np.kron(Rin,Iout)-np.kron(Iin,Rout), Q0, prec) if needST: Rh = np.kron(Rin,Iout) - np.kron(Iin,Rout) Qh = np.kron(Qin, Rout) + np.kron(Rin, Qout) massh, inih, Kh, cloh = GeneralFluidSolve (Qh, Rh, prec=prec) # sojourn time density in case of # srv0stop = false: inih*expm(Kh*x)*cloh*kron(Rin,Iout)/lambda # srv0stop = true: inih*expm(Kh*x)*cloh*kron(Rin,Rout)/lambda/mu lambd = np.sum(CTMCSolve(Qin)*Rin) mu = np.sum(CTMCSolve(Qout)*Rout) Ret = [] argIx = 0 while argIx<len(argv): if argIx in eaten: argIx += 1 continue elif type(argv[argIx]) is str and argv[argIx]=='flDistrPH': # transform it to PH Delta = Diag(Linsolve(K.T,-ini.T)) # Delta = diag (ini*inv(-K)); A = Delta.I*K.T*Delta alpha = np.sum(clo,1).T*Delta Ret.append(alpha) Ret.append(A) elif type(argv[argIx]) is str and argv[argIx]=='flDistrME': # transform it to ME B = SimilarityMatrixForVectors((-K).I*np.sum(clo,1), np.ones((K.shape[0],1))) Bi = B.I alpha = ini*Bi A = B*K*Bi Ret.append(alpha) Ret.append(A) elif type(argv[argIx]) is str and argv[argIx]=='flMoms': numOfMoms = argv[argIx+1] argIx += 1 moms = [] iK = -K.I for m in range(1,numOfMoms+1): moms.append(math.factorial(m)*np.sum(ini*iK**(m+1)*clo)) Ret.append(moms) elif type(argv[argIx]) is str and argv[argIx]=='flDistr': points = argv[argIx+1] argIx += 1 values = np.empty(points.shape) iK = -K.I for p in range(len(points.flat)): values.flat[p] = np.sum(mass0) + np.sum(ini*(ml.eye(K.shape[0])-la.expm(K*points.flat[p]))*iK*clo) Ret.append (values) elif type(argv[argIx]) is str and argv[argIx]=='stDistrPH': # convert result to PH representation Delta = Diag(Linsolve(Kh.T,-inih.T)) # Delta = diag (inih*inv(-Kh)); A = Delta.I*Kh.T*Delta if not srv0stop: alpha = np.sum(Delta*cloh*np.kron(Rin,Iout)/lambd,1).T else: alpha = np.sum(Delta*cloh*np.kron(Rin,Rout)/lambd/mu,1).T Ret.append(alpha) Ret.append(A) elif type(argv[argIx]) is str and argv[argIx]=='stDistrME': # convert result to ME representation if not srv0stop: B = SimilarityMatrixForVectors(np.sum(cloh*np.kron(Rin,Iout)/lambd,1), np.ones((Kh.shape[0],1))) else: B = SimilarityMatrixForVectors(np.sum(cloh*np.kron(Rin,Rout)/lambd/mu,1), np.ones((Kh.shape[0],1))) iB = B.I A = B*Kh*iB alpha = inih*(-Kh).I*iB Ret.append(alpha) Ret.append(A) elif type(argv[argIx]) is str and argv[argIx]=='stMoms': numOfMoms = argv[argIx+1] argIx += 1 moms = [] if srv0stop: kclo = cloh*np.kron(Rin,Rout)/lambd/mu else: kclo = cloh*np.kron(Rin,Iout)/lambd iKh = -Kh.I for m in range(1,numOfMoms+1): moms.append(math.factorial(m)*np.sum(inih*iKh**(m+1)*kclo)) Ret.append(moms) elif type(argv[argIx]) is str and argv[argIx]=='stDistr': points = argv[argIx+1] argIx += 1 values = np.empty(points.shape) if srv0stop: kclo = cloh*np.kron(Rin,Rout)/lambd/mu else: kclo = cloh*np.kron(Rin,Iout)/lambd iKh = -Kh.I for p in range(len(points.flat)): values.flat[p] = 1.0-np.sum(inih*la.expm(Kh*points.flat[p])*iKh*kclo) Ret.append(values) else: raise Exception ("FluFluQueue: Unknown parameter "+str(argv[argIx])) argIx += 1 if len(Ret)==1: return Ret[0] else: return Ret
def FluidQueue (Q, Rin, Rout, *argv): """ Returns various performane measures of a fluid queue. In a fluid queue there is a background continuous time Markov chain (given by generator Q), and diagonal matrix Rin (Rout) whose ith entry provides the fluid rate at which fluid enters the queue (can be served) while the background process is in state i. Parameters ---------- Q : matrix, shape (N,N) The generator of the background Markov chain Rin : matrix, shape (N,N) Diagonal matrix containing the fluid input rates associated to the states of the background process Rout : matrix, shape (N,N) Diagonal matrix containing the fluid output rates associated to the states of the background process further parameters : The rest of the function parameters specify the options and the performance measures to be computed. The supported performance measures and options in this function are: +----------------+--------------------+--------------------------------------+ | Parameter name | Input parameters | Output | +================+====================+======================================+ | "flMoms" | Number of moments | The moments of the fluid level | +----------------+--------------------+--------------------------------------+ | "flDistr" | A vector of points | The fluid level distribution at | | | | the requested points (cdf) | +----------------+--------------------+--------------------------------------+ | "flDistrME" | None | The vector-matrix parameters of the | | | | matrix-exponentially distributed | | | | fluid level distribution | +----------------+--------------------+--------------------------------------+ | "flDistrPH" | None | The vector-matrix parameters of the | | | | matrix-exponentially distributed | | | | fluid level distribution, converted | | | | to a PH representation | +----------------+--------------------+--------------------------------------+ | "stMoms" | Number of moments | The sojourn time moments of fluid | | | | drops | +----------------+--------------------+--------------------------------------+ | "stDistr" | A vector of points | The sojourn time distribution at the | | | | requested points (cummulative, cdf) | +----------------+--------------------+--------------------------------------+ | "stDistrME" | None | The vector-matrix parameters of the | | | | matrix-exponentially distributed | | | | sojourn time distribution | +----------------+--------------------+--------------------------------------+ | "stDistrPH" | None | The vector-matrix parameters of the | | | | matrix-exponentially distributed | | | | sojourn time distribution, converted | | | | to a PH representation | +----------------+--------------------+--------------------------------------+ | "prec" | The precision | Numerical precision to check if the | | | | input is valid and it is also used | | | | as a stopping condition when solving | | | | the Riccati equation | +----------------+--------------------+--------------------------------------+ | "Q0" | Matrix, shape(N,N) | The generator of the background | | | | Markov chain when the fluid level is | | | | zero. If not given, Q0=Q is assumed | +----------------+--------------------+--------------------------------------+ Returns ------- Ret : list of the performance measures Each entry of the list corresponds to a performance measure requested. If there is just a single item, then it is not put into a list. Notes ----- "flDistrME" and "stDistrME" behave much better numerically than "flDistrPH" and "stDistrPH". """ # parse options prec = 1e-14 needST = False Q0 = [] eaten = [] for i in range(len(argv)): if type(argv[i]) is str and argv[i]=="prec": prec = argv[i+1] eaten.append(i) eaten.append(i+1) elif type(argv[i]) is str and argv[i]=="Q0": Q0 = argv[i+1] eaten.append(i) eaten.append(i+1) elif type(argv[i]) is str and len(argv[i])>2 and argv[i][0:2]=="st": needST = True if butools.checkInput and not CheckGenerator(Q,False): raise Exception('FluidQueue: Generator matrix Q is not Markovian!') if butools.checkInput and len(Q0)>0 and not CheckGenerator(Q0,False): raise Exception('FluidQueue: Generator matrix Q0 is not Markovian!') if butools.checkInput and (np.any(np.diag(Rin)<-butools.checkPrecision) or np.any(np.diag(Rout)<-butools.checkPrecision)): raise Exception('FluidQueue: Fluid rates Rin and Rout must be non-negative !') mass0, ini, K, clo = GeneralFluidSolve (Q, Rin-Rout, Q0, prec) if needST: N = Q.shape[0] iniKi = Linsolve(K.T,-ini.T).T # iniki = ini*inv(-K); lambd = np.sum(mass0*Rin + iniKi*clo*Rin) Ret = [] argIx = 0 while argIx<len(argv): if argIx in eaten: argIx += 1 continue elif type(argv[argIx]) is str and argv[argIx]=='flDistrPH': # transform it to PH Delta = Diag(Linsolve(K.T,-ini.T)) # Delta = diag (ini*inv(-K)); A = Delta.I*K.T*Delta alpha = np.sum(clo,1).T*Delta Ret.append(alpha) Ret.append(A) elif type(argv[argIx]) is str and argv[argIx]=='flDistrME': # transform it to ME B = SimilarityMatrixForVectors((-K).I*np.sum(clo,1), np.ones((K.shape[0],1))) Bi = B.I alpha = ini*Bi A = B*K*Bi Ret.append(alpha) Ret.append(A) elif type(argv[argIx]) is str and argv[argIx]=='flMoms': numOfMoms = argv[argIx+1] argIx += 1 moms = [] iK = -K.I for m in range(1,numOfMoms+1): moms.append(math.factorial(m)*np.sum(ini*iK**(m+1)*clo)) Ret.append(moms) elif type(argv[argIx]) is str and argv[argIx]=='flDistr': points = argv[argIx+1] argIx += 1 values = np.empty(points.shape) iK = -K.I for p in range(len(points.flat)): values.flat[p] = np.sum(mass0) + np.sum(ini*(ml.eye(K.shape[0])-la.expm(K*points.flat[p]))*iK*clo) Ret.append (values) elif type(argv[argIx]) is str and argv[argIx]=='stDistrPH': # transform it to PH Delta = Diag(iniKi/lambd) alpha = np.reshape(clo*Rin,(1,N*len(ini.flat)),'F')*np.kron(ml.eye(N),Delta) A = np.kron(Rout, Delta.I*K.T*Delta) + np.kron(Q, ml.eye(K.shape[0])) Ret.append(alpha) Ret.append(A) elif type(argv[argIx]) is str and argv[argIx]=='stDistrME': B = SimilarityMatrixForVectors(np.reshape(-K.I*clo*Rin,(N*ini.size,1),'F'), np.ones((N*ini.size,1))) Bi = B.I alpha = np.kron(ml.ones((1,N)), ini/lambd)*Bi A = B*(np.kron(Q.T,ml.eye(K.shape[0])) + np.kron(Rout,K))*Bi Ret.append(alpha) Ret.append(A) elif type(argv[argIx]) is str and argv[argIx]=='stMoms': numOfMoms = argv[argIx+1] argIx += 1 moms = [] Z = np.kron(Q.T,ml.eye(K.shape[0])) + np.kron(Rout,K) iZ = -Z.I kini = np.kron(ml.ones((1,N)), ini/lambd) kclo = np.reshape(-K.I*clo*Rin,(N*ini.size,1),'F') for m in range(1,numOfMoms+1): moms.append(math.factorial(m)*np.sum(kini*iZ**(m+1)*(-Z)*kclo)) Ret.append(moms) elif type(argv[argIx]) is str and argv[argIx]=='stDistr': points = argv[argIx+1] argIx += 1 values = np.empty(points.shape) Z = np.kron(Q.T,ml.eye(K.shape[0])) + np.kron(Rout,K) kini = np.kron(ml.ones((1,N)), ini/lambd) kclo = np.reshape(-K.I*clo*Rin,(N*ini.size,1),'F') for p in range(len(points.flat)): values.flat[p] = 1.0-np.sum(kini*la.expm(Z*points.flat[p])*kclo) Ret.append(values) else: raise Exception ("FluidQueue: Unknown parameter "+str(argv[argIx])) argIx += 1 if len(Ret)==1: return Ret[0] else: return Ret
def MMAPPH1FCFS(D, sigma, S, *argv): """ Returns various performane measures of a MMAP[K]/PH[K]/1 first-come-first-serve queue, see [1]_. Parameters ---------- D : list of matrices of shape (N,N), length (K+1) The D0...DK matrices of the arrival process. sigma : list of row vectors, length (K) The list containing the initial probability vectors of the service time distributions of the various customer types. The length of the vectors does not have to be the same. S : list of square matrices, length (K) The transient generators of the phase type distributions representing the service time of the jobs belonging to various types. further parameters : The rest of the function parameters specify the options and the performance measures to be computed. The supported performance measures and options in this function are: +----------------+--------------------+----------------------------------------+ | Parameter name | Input parameters | Output | +================+====================+========================================+ | "ncMoms" | Number of moments | The moments of the number of customers | +----------------+--------------------+----------------------------------------+ | "ncDistr" | Upper limit K | The distribution of the number of | | | | customers from level 0 to level K-1 | +----------------+--------------------+----------------------------------------+ | "stMoms" | Number of moments | The sojourn time moments | +----------------+--------------------+----------------------------------------+ | "stDistr" | A vector of points | The sojourn time distribution at the | | | | requested points (cummulative, cdf) | +----------------+--------------------+----------------------------------------+ | "stDistrME" | None | The vector-matrix parameters of the | | | | matrix-exponentially distributed | | | | sojourn time distribution | +----------------+--------------------+----------------------------------------+ | "stDistrPH" | None | The vector-matrix parameters of the | | | | matrix-exponentially distributed | | | | sojourn time distribution, converted | | | | to a continuous PH representation | +----------------+--------------------+----------------------------------------+ | "prec" | The precision | Numerical precision used as a stopping | | | | condition when solving the Riccati | | | | equation | +----------------+--------------------+----------------------------------------+ | "classes" | Vector of integers | Only the performance measures | | | | belonging to these classes are | | | | returned. If not given, all classes | | | | are analyzed. | +----------------+--------------------+----------------------------------------+ (The quantities related to the number of customers in the system include the customer in the server, and the sojourn time related quantities include the service times as well) Returns ------- Ret : list of the performance measures Each entry of the list corresponds to a performance measure requested. Each entry is a matrix, where the columns belong to the various job types. If there is just a single item, then it is not put into a list. References ---------- .. [1] Qiming He, "Analysis of a continuous time SM[K]/PH[K]/1/FCFS queue: Age process, sojourn times, and queue lengths", Journal of Systems Science and Complexity, 25(1), pp 133-155, 2012. """ K = len(D) - 1 # parse options eaten = [] precision = 1e-14 classes = np.arange(0, K) for i in range(len(argv)): if argv[i] == "prec": precision = argv[i + 1] eaten.append(i) eaten.append(i + 1) elif argv[i] == "classes": classes = np.array(argv[i + 1]) - 1 eaten.append(i) eaten.append(i + 1) if butools.checkInput and not CheckMMAPRepresentation(D): raise Exception( 'MMAPPH1FCFS: The arrival process is not a valid MMAP representation!' ) if butools.checkInput: for k in range(K): if not CheckPHRepresentation(sigma[k], S[k]): raise Exception( 'MMAPPH1FCFS: the vector and matrix describing the service times is not a valid PH representation!' ) # some preparation D0 = D[0] N = D0.shape[0] Ia = ml.eye(N) Da = ml.zeros((N, N)) for q in range(K): Da += D[q + 1] theta = CTMCSolve(D0 + Da) beta = [CTMCSolve(S[k] + ml.sum(-S[k], 1) * sigma[k]) for k in range(K)] lambd = [np.sum(theta * D[k + 1]) for k in range(K)] mu = [np.sum(beta[k] * (-S[k])) for k in range(K)] Nsk = [S[k].shape[0] for k in range(K)] ro = np.sum(np.array(lambd) / np.array(mu)) alpha = theta * Da / sum(lambd) D0i = (-D0).I Sa = S[0] sa = [ml.zeros(sigma[0].shape)] * K sa[0] = sigma[0] ba = [ml.zeros(beta[0].shape)] * K ba[0] = beta[0] sv = [ml.zeros((Nsk[0], 1))] * K sv[0] = ml.sum(-S[0], 1) Pk = [D0i * D[q + 1] for q in range(K)] for k in range(1, K): Sa = la.block_diag(Sa, S[k]) for q in range(K): if q == k: sa[q] = ml.hstack((sa[q], sigma[k])) ba[q] = ml.hstack((ba[q], beta[k])) sv[q] = ml.vstack((sv[q], -np.sum(S[k], 1))) else: sa[q] = ml.hstack((sa[q], ml.zeros(sigma[k].shape))) ba[q] = ml.hstack((ba[q], ml.zeros(beta[k].shape))) sv[q] = ml.vstack((sv[q], ml.zeros((Nsk[k], 1)))) Sa = ml.matrix(Sa) P = D0i * Da iVec = ml.kron(D[1], sa[0]) for k in range(1, K): iVec += ml.kron(D[k + 1], sa[k]) Ns = Sa.shape[0] Is = ml.eye(Ns) # step 1. solve the age process of the queue # ========================================== # solve Y0 and calculate T Y0 = FluidFundamentalMatrices(ml.kron(Ia, Sa), ml.kron(Ia, -ml.sum(Sa, 1)), iVec, D0, "P", precision) T = ml.kron(Ia, Sa) + Y0 * iVec # calculate pi0 and v0 pi0 = ml.zeros((1, T.shape[0])) for k in range(K): pi0 += ml.kron(theta * D[k + 1], ba[k] / mu[k]) pi0 = -pi0 * T iT = (-T).I oa = ml.ones((N, 1)) # step 2. calculate performance measures # ====================================== Ret = [] for k in classes: argIx = 0 clo = iT * ml.kron(oa, sv[k]) while argIx < len(argv): if argIx in eaten: argIx += 1 continue elif type(argv[argIx]) is str and argv[argIx] == "stMoms": numOfSTMoms = argv[argIx + 1] rtMoms = [] for m in range(1, numOfSTMoms + 1): rtMoms.append( math.factorial(m) * np.sum(pi0 * iT**m * clo / (pi0 * clo))) Ret.append(rtMoms) argIx += 1 elif type(argv[argIx]) is str and argv[argIx] == "stDistr": stCdfPoints = argv[argIx + 1] cdf = [] for t in stCdfPoints: pr = 1 - np.sum(pi0 * la.expm(T * t) * clo / (pi0 * clo)) cdf.append(pr) Ret.append(np.array(cdf)) argIx += 1 elif type(argv[argIx]) is str and argv[argIx] == "stDistrME": Bm = SimilarityMatrixForVectors(clo / (pi0 * clo), ml.ones((N * Ns, 1))) Bmi = Bm.I A = Bm * T * Bmi alpha = pi0 * Bmi Ret.append(alpha) Ret.append(A) elif type(argv[argIx]) is str and argv[argIx] == "stDistrPH": vv = pi0 * iT ix = np.arange(N * Ns) nz = ix[vv.flat > precision] delta = Diag(vv[:, nz]) cl = -T * clo / (pi0 * clo) alpha = cl[nz, :].T * delta A = delta.I * T[nz, :][:, nz].T * delta Ret.append(alpha) Ret.append(A) elif type(argv[argIx]) is str and argv[argIx] == "ncDistr": numOfQLProbs = argv[argIx + 1] argIx += 1 values = np.empty(numOfQLProbs) jm = ml.zeros((Ns, 1)) jm[np.sum(Nsk[0:k]):np.sum(Nsk[0:k + 1]), :] = 1 jmc = ml.ones((Ns, 1)) jmc[np.sum(Nsk[0:k]):np.sum(Nsk[0:k + 1]), :] = 0 LmCurr = la.solve_sylvester(T, ml.kron(D0 + Da - D[k + 1], Is), -ml.eye(N * Ns)) values[0] = 1 - ro + np.sum(pi0 * LmCurr * ml.kron(oa, jmc)) for i in range(1, numOfQLProbs): LmPrev = LmCurr LmCurr = la.solve_sylvester( T, ml.kron(D0 + Da - D[k + 1], Is), -LmPrev * ml.kron(D[k + 1], Is)) values[i] = np.sum(pi0 * LmCurr * ml.kron(oa, jmc) + pi0 * LmPrev * ml.kron(oa, jm)) Ret.append(values) elif type(argv[argIx]) is str and argv[argIx] == "ncMoms": numOfQLMoms = argv[argIx + 1] argIx += 1 jm = ml.zeros((Ns, 1)) jm[np.sum(Nsk[0:k]):np.sum(Nsk[0:k + 1]), :] = 1 ELn = [ la.solve_sylvester(T, ml.kron(D0 + Da, Is), -ml.eye(N * Ns)) ] qlMoms = [] for n in range(1, numOfQLMoms + 1): bino = 1 Btag = ml.zeros((N * Ns, N * Ns)) for i in range(n): Btag += bino * ELn[i] bino *= (n - i) / (i + 1) ELn.append( la.solve_sylvester(T, ml.kron(D0 + Da, Is), -Btag * ml.kron(D[k + 1], Is))) qlMoms.append( np.sum(pi0 * ELn[n]) + np.sum(pi0 * Btag * ml.kron(oa, jm))) Ret.append(qlMoms) else: raise Exception("MMAPPH1FCFS: Unknown parameter " + str(argv[argIx])) argIx += 1 if len(Ret) == 1: return Ret[0] else: return Ret
def RandomMMAP(order, types, mean=1.0, zeroEntries=0, maxTrials=1000, prec=1e-7): """ Returns a random Markovian arrival process with given mean value. Parameters ---------- order : int The size of the MAP types : int The number of different arrival types mean : double, optional The mean inter-arrival times of the MMAP zeroEntries : int, optional The number of zero entries in the D0 and D1 matrices maxTrials : int, optional The maximum number of trials to find a proper MMAP (that has an irreducible phase process and none of its parameters is all-zero) prec : double, optional Numerical precision for checking the irreducibility. The default value is 1e-14. Returns ------- D : list/cell of matrices of shape(M,M), length(types+1) The D0...Dtypes matrices of the MMAP """ # distribute the zero entries among the rows def allZeroDistr(states, zeros): if states == 1: return [[zeros]] else: o = [] for i in range(zeros + 1): x = allZeroDistr(states - 1, zeros - i) for j in range(len(x)): xt = x[j] xt.append(i) xt.sort() # check if we have it already if o.count(xt) == 0: o.append(xt) return o if zeroEntries > (types + 1) * order * order - 2 * order: raise Exception( "RandomMAP/MMAP: Too many zero entries requested! Try to decrease the zeroEntries parameter!" ) zeroDistr = allZeroDistr(order, zeroEntries) trials = 1 while trials < maxTrials: # select a configuration from zeroDistr: it is a list describing the zero entries in each row zdix = np.random.permutation(len(zeroDistr)) for k in range(len(zeroDistr)): zDistr = zeroDistr[zdix[k]] bad = False for d in zDistr: if d >= (types + 1) * order - 1: bad = True break if bad: continue B = np.zeros((order, (types + 1) * order)) for i in range(order): rp = np.random.permutation((types + 1) * order - 1) a = np.zeros((types + 1) * order - 1) for j in range((types + 1) * order - 1 - zDistr[i]): a[rp[j]] = np.random.rand() B[i, 0:i] = a[0:i] B[i, i + 1:] = a[i:] # construct MMAP matrices D = [] for i in range(types + 1): Di = ml.matrix(B[:, i * order:(i + 1) * order]) D.append(Di) D[0] -= Diag(np.sum(B, 1)) # check if it is a proper MAP (irreducible phase process & no full zero matrix) sumD = ml.zeros((order, order)) for i in range(len(D)): sumD += D[i] if la.matrix_rank( D[0]) == order and la.matrix_rank(sumD) == order - 1: alpha = CTMCSolve(sumD) if np.min(np.abs(alpha)) > prec: fullZero = False for Di in D: if np.all(Di == 0.0): fullZero = True break if not fullZero: # scale to the mean value m = MarginalMomentsFromMMAP(D, 1)[0] for i in range(types + 1): D[i] *= m / mean return D trials += 1 raise Exception( "No feasible random MAP/MMAP found with such many zero entries! Try to increase the maxTrials parameter!" )