def LagkJointMomentsFromMMAP(D, K=0, L=1): """ Returns the lag-L joint moments of a marked Markovian arrival process. Parameters ---------- D : list/cell of matrices of shape(M,M), length(N) The D0...DN matrices of the MMAP to check K : int, optional The dimension of the matrix of joint moments to compute. If K=0, the MxM joint moments will be computed. The default value is 0 L : int, optional The lag at which the joint moments are computed. The default value is 1 prec : double, optional Numerical precision to check if the input is valid. The default value is 1e-14 Returns ------- Nm : list/cell of matrices of shape(K+1,K+1), length(L) The matrices containing the lag-L joint moments, starting from moment 0. """ if butools.checkInput and not CheckMMAPRepresentation(D): raise Exception( "LagkJointMomentsFromMMAP: Input is not a valid MMAP representation!" ) return LagkJointMomentsFromMRAP(D, K, L)
def MarginalMomentsFromMMAP(D, K=0): """ Returns the moments of the marginal distribution of a marked Markovian arrival process. Parameters ---------- D : list/cell of matrices of shape(M,M), length(N) The D0...DN matrices of the MMAP K : int, optional Number of moments to compute. If K=0, 2*M-1 moments are computed. The default value is K=0. precision : double, optional Numerical precision for checking if the input is valid. The default value is 1e-14 Returns ------- moms : row vector of doubles, length K The vector of moments. """ if butools.checkInput and not CheckMMAPRepresentation(D): raise Exception( "MarginalMomentsFromMMAP: Input is not a valid MMAP representation!" ) alpha, A = MarginalDistributionFromMMAP(D) return MomentsFromPH(alpha, A, K)
def MarginalDistributionFromMMAP(D): """ Returns the phase type distributed marginal distribution of a marked Markovian arrival process. Parameters ---------- D : list/cell of matrices of shape(M,M), length(N) The D0...DN matrices of the MMAP precision : double, optional Numerical precision for checking if the input is valid. The default value is 1e-14 Returns ------- alpha : matrix, shape (1,M) The initial probability vector of the phase type distributed marginal distribution A : matrix, shape (M,M) The transient generator of the phase type distributed marginal distribution """ if butools.checkInput and not CheckMMAPRepresentation(D): raise Exception( "MarginalDistributionFromMMAP: Input is not a valid MMAP representation!" ) return MarginalDistributionFromMRAP(D)
def MMAPPH1NPPR(D, sigma, S, *argv): """ Returns various performane measures of a continuous time MMAP[K]/PH[K]/1 non-preemptive priority queue, see [1]_. Parameters ---------- D : list of matrices of shape (N,N), length (K+1) The D0...DK matrices of the arrival process. D1 corresponds to the lowest, DK to the highest priority. 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) | +----------------+--------------------+----------------------------------------+ | "prec" | The precision | Numerical precision used as a stopping | | | | condition when solving the Riccati and | | | | the matrix-quadratic equations | +----------------+--------------------+----------------------------------------+ | "erlMaxOrder" | Integer number | The maximal Erlang order used in the | | | | erlangization procedure. The default | | | | value is 200. | +----------------+--------------------+----------------------------------------+ | "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] G. Horvath, "Efficient analysis of the MMAP[K]/PH[K]/1 priority queue", European Journal of Operational Research, 246(1), 128-139, 2015. """ K = len(D) - 1 # parse options eaten = [] erlMaxOrder = 200 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] == "erlMaxOrder": erlMaxOrder = 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( 'MMAPPH1PRPR: 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( 'MMAPPH1PRPR: the vector and matrix describing the service times is not a valid PH representation!' ) # some preparation D0 = D[0] N = D0.shape[0] I = ml.eye(N) sD = ml.zeros((N, N)) for Di in D: sD += Di s = [] M = np.empty(K) for i in range(K): s.append(np.sum(-S[i], 1)) M[i] = sigma[i].size # step 1. solution of the workload process of the joint queue # =========================================================== sM = np.sum(M) Qwmm = ml.matrix(D0) Qwpm = ml.zeros((N * sM, N)) Qwmp = ml.zeros((N, N * sM)) Qwpp = ml.zeros((N * sM, N * sM)) kix = 0 for i in range(K): Qwmp[:, kix:kix + N * M[i]] = np.kron(D[i + 1], sigma[i]) Qwpm[kix:kix + N * M[i], :] = np.kron(I, s[i]) Qwpp[kix:kix + N * M[i], :][:, kix:kix + N * M[i]] = np.kron(I, S[i]) kix += N * M[i] # calculate fundamental matrices Psiw, Kw, Uw = FluidFundamentalMatrices(Qwpp, Qwpm, Qwmp, Qwmm, 'PKU', precision) # calculate boundary vector Ua = ml.ones((N, 1)) + 2 * np.sum(Qwmp * (-Kw).I, 1) pm = Linsolve( ml.hstack((Uw, Ua)).T, ml.hstack((ml.zeros((1, N)), ml.ones((1, 1)))).T).T ro = ((1.0 - np.sum(pm)) / 2.0) / ( np.sum(pm) + (1.0 - np.sum(pm)) / 2.0 ) # calc idle time with weight=1, and the busy time with weight=1/2 kappa = pm / np.sum(pm) pi = CTMCSolve(sD) lambd = [] for i in range(K): lambd.append(np.sum(pi * D[i + 1])) Psiw = [] Qwmp = [] Qwzp = [] Qwpp = [] Qwmz = [] Qwpz = [] Qwzz = [] Qwmm = [] Qwpm = [] Qwzm = [] for k in range(K): # step 2. construct a workload process for classes k...K # ====================================================== Mlo = np.sum(M[:k]) Mhi = np.sum(M[k:]) Qkwpp = ml.zeros((N * Mlo * Mhi + N * Mhi, N * Mlo * Mhi + N * Mhi)) Qkwpz = ml.zeros((N * Mlo * Mhi + N * Mhi, N * Mlo)) Qkwpm = ml.zeros((N * Mlo * Mhi + N * Mhi, N)) Qkwmz = ml.zeros((N, N * Mlo)) Qkwmp = ml.zeros((N, N * Mlo * Mhi + N * Mhi)) Dlo = ml.matrix(D0) for i in range(k): Dlo = Dlo + D[i + 1] Qkwmm = Dlo Qkwzp = ml.zeros((N * Mlo, N * Mlo * Mhi + N * Mhi)) Qkwzm = ml.zeros((N * Mlo, N)) Qkwzz = ml.zeros((N * Mlo, N * Mlo)) kix = 0 for i in range(k, K): kix2 = 0 for j in range(k): bs = N * M[j] * M[i] bs2 = N * M[j] Qkwpp[kix:kix + bs, kix:kix + bs] = np.kron(I, np.kron(ml.eye(M[j]), S[i])) Qkwpz[kix:kix + bs, kix2:kix2 + bs2] = np.kron(I, np.kron(ml.eye(M[j]), s[i])) Qkwzp[kix2:kix2 + bs2, kix:kix + bs] = np.kron(D[i + 1], np.kron(ml.eye(M[j]), sigma[i])) kix += bs kix2 += bs2 for i in range(k, K): bs = N * M[i] Qkwpp[kix:kix + bs, :][:, kix:kix + bs] = np.kron(I, S[i]) Qkwpm[kix:kix + bs, :] = np.kron(I, s[i]) Qkwmp[:, kix:kix + bs] = np.kron(D[i + 1], sigma[i]) kix += bs kix = 0 for j in range(k): bs = N * M[j] Qkwzz[kix:kix + bs, kix:kix + bs] = np.kron(Dlo, ml.eye(M[j])) + np.kron(I, S[j]) Qkwzm[kix:kix + bs, :] = np.kron(I, s[j]) kix += bs if Qkwzz.shape[0] > 0: Psikw = FluidFundamentalMatrices( Qkwpp + Qkwpz * (-Qkwzz).I * Qkwzp, Qkwpm + Qkwpz * (-Qkwzz).I * Qkwzm, Qkwmp, Qkwmm, 'P', precision) else: Psikw = FluidFundamentalMatrices(Qkwpp, Qkwpm, Qkwmp, Qkwmm, 'P', precision) Psiw.append(Psikw) Qwzp.append(Qkwzp) Qwmp.append(Qkwmp) Qwpp.append(Qkwpp) Qwmz.append(Qkwmz) Qwpz.append(Qkwpz) Qwzz.append(Qkwzz) Qwmm.append(Qkwmm) Qwpm.append(Qkwpm) Qwzm.append(Qkwzm) # step 3. calculate Phi vectors # ============================= lambdaS = sum(lambd) phi = [(1 - ro) * kappa * (-D0) / lambdaS] q0 = [[]] qL = [[]] for k in range(K - 1): sDk = ml.matrix(D0) for j in range(k + 1): sDk = sDk + D[j + 1] # pk pk = sum(lambd[:k + 1]) / lambdaS - (1 - ro) * kappa * np.sum( sDk, 1) / lambdaS # A^(k,1) Qwzpk = Qwzp[k + 1] vix = 0 Ak = [] for ii in range(k + 1): bs = N * M[ii] V1 = Qwzpk[vix:vix + bs, :] Ak.append( np.kron(I, sigma[ii]) * (-np.kron(sDk, ml.eye(M[ii])) - np.kron(I, S[ii])).I * (np.kron(I, s[ii]) + V1 * Psiw[k + 1])) vix += bs # B^k Qwmpk = Qwmp[k + 1] Bk = Qwmpk * Psiw[k + 1] ztag = phi[0] * ((-D0).I * D[k + 1] * Ak[k] - Ak[0] + (-D0).I * Bk) for i in range(k): ztag += phi[i + 1] * (Ak[i] - Ak[i + 1]) + phi[0] * ( -D0).I * D[i + 1] * Ak[i] Mx = ml.eye(Ak[k].shape[0]) - Ak[k] Mx[:, 0] = ml.ones((N, 1)) phi.append( ml.hstack((pk, ztag[:, 1:])) * Mx.I) # phi(k) = Psi^(k)_k * p(k). Psi^(k)_i = phi(i) / p(k) q0.append(phi[0] * (-D0).I) qLii = [] for ii in range(k + 1): qLii.append((phi[ii + 1] - phi[ii] + phi[0] * (-D0).I * D[ii + 1]) * np.kron(I, sigma[ii]) * (-np.kron(sDk, ml.eye(M[ii])) - np.kron(I, S[ii])).I) qL.append(ml.hstack(qLii)) # step 4. calculate performance measures # ====================================== Ret = [] for k in classes: sD0k = ml.matrix(D0) for i in range(k): sD0k += D[i + 1] if k < K - 1: # step 4.1 calculate distribution of the workload process right # before the arrivals of class k jobs # ============================================================ if Qwzz[k].shape[0] > 0: Kw = Qwpp[k] + Qwpz[k] * ( -Qwzz[k]).I * Qwzp[k] + Psiw[k] * Qwmp[k] else: Kw = Qwpp[k] + Psiw[k] * Qwmp[k] BM = ml.zeros((0, 0)) CM = ml.zeros((0, N)) DM = ml.zeros((0, 0)) for i in range(k): BM = la.block_diag(BM, np.kron(I, S[i])) CM = ml.vstack((CM, np.kron(I, s[i]))) DM = la.block_diag(DM, np.kron(D[k + 1], ml.eye(M[i]))) if k > 0: Kwu = ml.vstack((ml.hstack( (Kw, (Qwpz[k] + Psiw[k] * Qwmz[k]) * (-Qwzz[k]).I * DM)), ml.hstack((ml.zeros( (BM.shape[0], Kw.shape[1])), BM)))) Bwu = ml.vstack((Psiw[k] * D[k + 1], CM)) iniw = ml.hstack( (q0[k] * Qwmp[k] + qL[k] * Qwzp[k], qL[k] * DM)) pwu = q0[k] * D[k + 1] else: Kwu = Kw Bwu = Psiw[k] * D[k + 1] iniw = pm * Qwmp[k] pwu = pm * D[k + 1] norm = np.sum(pwu) + np.sum(iniw * (-Kwu).I * Bwu) pwu = pwu / norm iniw = iniw / norm # step 4.2 create the fluid model whose first passage time equals the # WAITING time of the low prioroity customers # ================================================================== KN = Kwu.shape[0] Qspp = ml.zeros( (KN + N * np.sum(M[k + 1:]), KN + N * np.sum(M[k + 1:]))) Qspm = ml.zeros((KN + N * np.sum(M[k + 1:]), N)) Qsmp = ml.zeros((N, KN + N * np.sum(M[k + 1:]))) Qsmm = sD0k + D[k + 1] kix = 0 for i in range(k + 1, K): bs = N * M[i] Qspp[KN + kix:KN + kix + bs, :][:, KN + kix:KN + kix + bs] = np.kron(I, S[i]) Qspm[KN + kix:KN + kix + bs, :] = np.kron(I, s[i]) Qsmp[:, KN + kix:KN + kix + bs] = np.kron(D[i + 1], sigma[i]) kix += bs Qspp[:KN, :][:, :KN] = Kwu Qspm[:KN, :] = Bwu inis = ml.hstack((iniw, ml.zeros((1, N * np.sum(M[k + 1:]))))) # calculate fundamental matrix Psis = FluidFundamentalMatrices(Qspp, Qspm, Qsmp, Qsmm, 'P', precision) # step 4.3. calculate the performance measures # ========================================== argIx = 0 while argIx < len(argv): if argIx in eaten: argIx += 1 continue elif type(argv[argIx]) is str and argv[argIx] == "stMoms": # MOMENTS OF THE SOJOURN TIME # ~~~~~~~~~~~~~~~~~~~~~~~~~~~ numOfSTMoms = argv[argIx + 1] # calculate waiting time moments Pn = [Psis] wtMoms = [] for n in range(1, numOfSTMoms + 1): A = Qspp + Psis * Qsmp B = Qsmm + Qsmp * Psis C = -2 * n * Pn[n - 1] bino = 1 for i in range(1, n): bino = bino * (n - i + 1) / i C += bino * Pn[i] * Qsmp * Pn[n - i] P = la.solve_sylvester(A, B, -C) Pn.append(P) wtMoms.append(np.sum(inis * P * (-1)**n) / 2**n) # calculate RESPONSE time moments Pnr = [np.sum(inis * Pn[0]) * sigma[k]] rtMoms = [] for n in range(1, numOfSTMoms + 1): P = n * Pnr[n - 1] * (-S[k]).I + (-1)**n * np.sum( inis * Pn[n]) * sigma[k] / 2**n Pnr.append(P) rtMoms.append( np.sum(P) + np.sum(pwu) * math.factorial(n) * np.sum(sigma[k] * (-S[k]).I**n)) Ret.append(rtMoms) argIx += 1 elif type(argv[argIx]) is str and argv[argIx] == "stDistr": # DISTRIBUTION OF THE SOJOURN TIME # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ stCdfPoints = argv[argIx + 1] res = [] for t in stCdfPoints: L = erlMaxOrder lambdae = L / t / 2 Psie = FluidFundamentalMatrices( Qspp - lambdae * ml.eye(Qspp.shape[0]), Qspm, Qsmp, Qsmm - lambdae * ml.eye(Qsmm.shape[0]), 'P', precision) Pn = [Psie] pr = (np.sum(pwu) + np.sum(inis * Psie)) * (1 - np.sum( sigma[k] * (ml.eye(S[k].shape[0]) - S[k] / 2 / lambdae).I**L)) for n in range(1, L): A = Qspp + Psie * Qsmp - lambdae * ml.eye( Qspp.shape[0]) B = Qsmm + Qsmp * Psie - lambdae * ml.eye( Qsmm.shape[0]) C = 2 * lambdae * Pn[n - 1] for i in range(1, n): C += Pn[i] * Qsmp * Pn[n - i] P = la.solve_sylvester(A, B, -C) Pn.append(P) pr += np.sum(inis * P) * ( 1 - np.sum(sigma[k] * (np.eye(S[k].shape[0]) - S[k] / 2 / lambdae).I**(L - n))) res.append(pr) Ret.append(np.array(res)) argIx += 1 elif type(argv[argIx]) is str and (argv[argIx] == "ncMoms" or argv[argIx] == "ncDistr"): W = (-np.kron(sD - D[k + 1], ml.eye(M[k])) - np.kron(I, S[k])).I * np.kron(D[k + 1], ml.eye(M[k])) iW = (ml.eye(W.shape[0]) - W).I w = np.kron(ml.eye(N), sigma[k]) omega = (-np.kron(sD - D[k + 1], ml.eye(M[k])) - np.kron(I, S[k])).I * np.kron(I, s[k]) if argv[argIx] == "ncMoms": # MOMENTS OF THE NUMBER OF JOBS # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ numOfQLMoms = argv[argIx + 1] # first calculate it at departure instants Psii = [Psis] QLDPn = [inis * Psii[0] * w * iW] for n in range(1, numOfQLMoms + 1): A = Qspp + Psis * Qsmp B = Qsmm + Qsmp * Psis C = n * Psii[n - 1] * D[k + 1] bino = 1 for i in range(1, n): bino = bino * (n - i + 1) / i C = C + bino * Psii[i] * Qsmp * Psii[n - i] P = la.solve_sylvester(A, B, -C) Psii.append(P) QLDPn.append(n * QLDPn[n - 1] * iW * W + inis * P * w * iW) for n in range(numOfQLMoms + 1): QLDPn[n] = (QLDPn[n] + pwu * w * iW**(n + 1) * W**n) * omega # now calculate it at random time instance QLPn = [pi] qlMoms = [] iTerm = (ml.ones((N, 1)) * pi - sD).I for n in range(1, numOfQLMoms + 1): sumP = np.sum(QLDPn[n]) + n * np.sum( (QLDPn[n - 1] - QLPn[n - 1] * D[k + 1] / lambd[k]) * iTerm * D[k + 1]) P = sumP * pi + n * ( QLPn[n - 1] * D[k + 1] - QLDPn[n - 1] * lambd[k]) * iTerm QLPn.append(P) qlMoms.append(np.sum(P)) qlMoms = MomsFromFactorialMoms(qlMoms) Ret.append(qlMoms) argIx += 1 elif argv[argIx] == "ncDistr": # DISTRIBUTION OF THE NUMBER OF JOBS # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ numOfQLProbs = argv[argIx + 1] Psid = FluidFundamentalMatrices( Qspp, Qspm, Qsmp, sD0k, 'P', precision) Pn = [Psid] XDn = inis * Psid * w dqlProbs = (XDn + pwu * w) * omega for n in range(1, numOfQLProbs): A = Qspp + Psid * Qsmp B = sD0k + Qsmp * Psid C = Pn[n - 1] * D[k + 1] for i in range(1, n): C += Pn[i] * Qsmp * Pn[n - i] P = la.solve_sylvester(A, B, -C) Pn.append(P) XDn = XDn * W + inis * P * w dqlProbs = ml.vstack( (dqlProbs, (XDn + pwu * w * W**n) * omega)) # now calculate it at random time instance iTerm = -(sD - D[k + 1]).I qlProbs = lambd[k] * dqlProbs[0, :] * iTerm for n in range(1, numOfQLProbs): P = (qlProbs[n - 1, :] * D[k + 1] + lambd[k] * (dqlProbs[n, :] - dqlProbs[n - 1, :])) * iTerm qlProbs = ml.vstack((qlProbs, P)) qlProbs = np.sum(qlProbs, 1).A.flatten() Ret.append(qlProbs) argIx += 1 else: raise Exception("MMAPPH1NPPR: Unknown parameter " + str(argv[argIx])) argIx += 1 elif k == K - 1: # step 3. calculate the performance measures # ========================================== argIx = 0 while argIx < len(argv): if argIx in eaten: argIx += 1 continue elif type(argv[argIx]) is str and (argv[argIx] == "stMoms" or argv[argIx] == "stDistr"): Kw = Qwpp[k] + Qwpz[k] * ( -Qwzz[k]).I * Qwzp[k] + Psiw[k] * Qwmp[k] AM = ml.zeros((0, 0)) BM = ml.zeros((0, 0)) CM = ml.zeros((0, 1)) DM = ml.zeros((0, 0)) for i in range(k): AM = la.block_diag( AM, np.kron(ml.ones((N, 1)), np.kron(ml.eye(M[i]), s[k]))) BM = la.block_diag(BM, S[i]) CM = ml.vstack((CM, s[i])) DM = la.block_diag(DM, np.kron(D[k + 1], ml.eye(M[i]))) Z = ml.vstack((ml.hstack( (Kw, ml.vstack((AM, ml.zeros( (N * M[k], AM.shape[1])))))), ml.hstack((ml.zeros( (BM.shape[0], Kw.shape[1])), BM)))) z = ml.vstack((ml.zeros( (AM.shape[0], 1)), np.kron(ml.ones((N, 1)), s[k]), CM)) iniw = ml.hstack((q0[k] * Qwmp[k] + qL[k] * Qwzp[k], ml.zeros((1, BM.shape[0])))) zeta = iniw / np.sum(iniw * (-Z).I * z) if argv[argIx] == "stMoms": # MOMENTS OF THE SOJOURN TIME # ~~~~~~~~~~~~~~~~~~~~~~~~~~~ numOfSTMoms = argv[argIx + 1] rtMoms = [] for i in range(1, numOfSTMoms + 1): rtMoms.append( np.sum( math.factorial(i) * zeta * (-Z).I**(i + 1) * z)) Ret.append(rtMoms) argIx += 1 if argv[argIx] == "stDistr": # DISTRIBUTION OF THE SOJOURN TIME # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ stCdfPoints = argv[argIx + 1] rtDistr = [] for t in stCdfPoints: rtDistr.append( np.sum(zeta * (-Z).I * (ml.eye(Z.shape[0]) - la.expm(Z * t)) * z)) Ret.append(np.array(rtDistr)) argIx += 1 elif type(argv[argIx]) is str and (argv[argIx] == "ncMoms" or argv[argIx] == "ncDistr"): L = ml.zeros((N * np.sum(M), N * np.sum(M))) B = ml.zeros((N * np.sum(M), N * np.sum(M))) F = ml.zeros((N * np.sum(M), N * np.sum(M))) kix = 0 for i in range(K): bs = N * M[i] F[kix:kix + bs, :][:, kix:kix + bs] = np.kron( D[k + 1], ml.eye(M[i])) L[kix:kix + bs, :][:, kix:kix + bs] = np.kron( sD0k, ml.eye(M[i])) + np.kron(I, S[i]) if i < K - 1: L[kix:kix + bs, :][:, N * np.sum(M[:k]):] = np.kron( I, s[i] * sigma[k]) else: B[kix:kix + bs, :][:, N * np.sum(M[:k]):] = np.kron( I, s[i] * sigma[k]) kix += bs R = QBDFundamentalMatrices(B, L, F, 'R', precision) p0 = ml.hstack((qL[k], q0[k] * np.kron(I, sigma[k]))) p0 = p0 / np.sum(p0 * (ml.eye(R.shape[0]) - R).I) if argv[argIx] == "ncMoms": # MOMENTS OF THE NUMBER OF JOBS # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ numOfQLMoms = argv[argIx + 1] qlMoms = [] for i in range(1, numOfQLMoms + 1): qlMoms.append( np.sum( math.factorial(i) * p0 * R**i * (ml.eye(R.shape[0]) - R).I**(i + 1))) Ret.append(MomsFromFactorialMoms(qlMoms)) elif argv[argIx] == "ncDistr": # DISTRIBUTION OF THE NUMBER OF JOBS # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ numOfQLProbs = argv[argIx + 1] qlProbs = [np.sum(p0)] for i in range(1, numOfQLProbs): qlProbs.append(np.sum(p0 * R**i)) Ret.append(np.array(qlProbs)) argIx += 1 else: raise Exception("MMAPPH1NPPR: 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 MMAPPH1PRPR(D, sigma, S, *argv): """ Returns various performane measures of a MMAP[K]/PH[K]/1 preemptive resume priority queue, see [1]_. Parameters ---------- D : list of matrices of shape (N,N), length (K+1) The D0...DK matrices of the arrival process. D1 corresponds to the lowest, DK to the highest priority. 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) | +----------------+--------------------+----------------------------------------+ | "prec" | The precision | Numerical precision used as a stopping | | | | condition when solving the Riccati and | | | | the matrix-quadratic equations | +----------------+--------------------+----------------------------------------+ | "erlMaxOrder" | Integer number | The maximal Erlang order used in the | | | | erlangization procedure. The default | | | | value is 200. | +----------------+--------------------+----------------------------------------+ | "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] G. Horvath, "Efficient analysis of the MMAP[K]/PH[K]/1 priority queue", European Journal of Operational Research, 246(1), 128-139, 2015. """ K = len(D) - 1 # parse options eaten = [] erlMaxOrder = 200 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] == "erlMaxOrder": erlMaxOrder = 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( 'MMAPPH1PRPR: 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( 'MMAPPH1PRPR: the vector and matrix describing the service times is not a valid PH representation!' ) # some preparation D0 = D[0] N = D0.shape[0] I = ml.eye(N) sD = ml.zeros((N, N)) for Di in D: sD += Di s = [] M = np.empty(K) for i in range(K): s.append(np.sum(-S[i], 1)) M[i] = sigma[i].size Ret = [] for k in classes: # step 1. solution of the workload process of the system # ====================================================== sM = np.sum(M[k:K]) Qwmm = ml.matrix(D0) for i in range(k): Qwmm += D[i + 1] Qwpm = ml.zeros((N * sM, N)) Qwmp = ml.zeros((N, N * sM)) Qwpp = ml.zeros((N * sM, N * sM)) kix = 0 for i in range(k, K): Qwmp[:, kix:kix + N * M[i]] = np.kron(D[i + 1], sigma[i]) Qwpm[kix:kix + N * M[i], :] = np.kron(I, s[i]) Qwpp[kix:kix + N * M[i], :][:, kix:kix + N * M[i]] = np.kron(I, S[i]) kix += N * M[i] # calculate fundamental matrices Psiw, Kw, Uw = FluidFundamentalMatrices(Qwpp, Qwpm, Qwmp, Qwmm, 'PKU', precision) # calculate boundary vector Ua = ml.ones((N, 1)) + 2 * np.sum(Qwmp * (-Kw).I, 1) pm = Linsolve( ml.hstack((Uw, Ua)).T, ml.hstack((ml.zeros((1, N)), ml.ones((1, 1)))).T).T Bw = ml.zeros((N * sM, N)) Bw[0:N * M[k], :] = np.kron(I, s[k]) kappa = pm * Qwmp / np.sum(pm * Qwmp * (-Kw).I * Bw) if k < K - 1: # step 2. construct fluid model for the remaining sojourn time process # ==================================================================== # (for each class except the highest priority) Qsmm = ml.matrix(D0) for i in range(k + 1): Qsmm += D[i + 1] Np = Kw.shape[0] Qspm = ml.zeros((Np + N * np.sum(M[k + 1:]), N)) Qsmp = ml.zeros((N, Np + N * np.sum(M[k + 1:]))) Qspp = ml.zeros( (Np + N * np.sum(M[k + 1:]), Np + N * np.sum(M[k + 1:]))) Qspp[:Np, :Np] = Kw Qspm[:Np, :N] = Bw kix = Np for i in range(k + 1, K): Qsmp[:, kix:kix + N * M[i]] = np.kron(D[i + 1], sigma[i]) Qspm[kix:kix + N * M[i], :] = np.kron(I, s[i]) Qspp[kix:kix + N * M[i], kix:kix + N * M[i]] = np.kron(I, S[i]) kix += N * M[i] inis = ml.hstack((kappa, ml.zeros((1, N * np.sum(M[k + 1:]))))) Psis = FluidFundamentalMatrices(Qspp, Qspm, Qsmp, Qsmm, 'P', precision) # step 3. calculate the performance measures # ========================================== argIx = 0 while argIx < len(argv): if argIx in eaten: argIx += 1 continue elif type(argv[argIx]) is str and argv[argIx] == "stMoms": # MOMENTS OF THE SOJOURN TIME # ~~~~~~~~~~~~~~~~~~~~~~~~~~~ numOfSTMoms = argv[argIx + 1] Pn = [Psis] rtMoms = [] for n in range(1, numOfSTMoms + 1): A = Qspp + Psis * Qsmp B = Qsmm + Qsmp * Psis C = -2 * n * Pn[n - 1] bino = 1 for i in range(1, n): bino = bino * (n - i + 1) / i C += bino * Pn[i] * Qsmp * Pn[n - i] P = la.solve_sylvester(A, B, -C) Pn.append(P) rtMoms.append(np.sum(inis * P * (-1)**n) / 2**n) Ret.append(rtMoms) argIx += 1 elif type(argv[argIx]) is str and argv[argIx] == "stDistr": # DISTRIBUTION OF THE SOJOURN TIME # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ stCdfPoints = argv[argIx + 1] res = [] for t in stCdfPoints: L = erlMaxOrder lambd = L / t / 2 Psie = FluidFundamentalMatrices( Qspp - lambd * ml.eye(Qspp.shape[0]), Qspm, Qsmp, Qsmm - lambd * ml.eye(Qsmm.shape[0]), 'P', precision) Pn = [Psie] pr = np.sum(inis * Psie) for n in range(1, L): A = Qspp + Psie * Qsmp - lambd * ml.eye( Qspp.shape[0]) B = Qsmm + Qsmp * Psie - lambd * ml.eye( Qsmm.shape[0]) C = 2 * lambd * Pn[n - 1] for i in range(1, n): C += Pn[i] * Qsmp * Pn[n - i] P = la.solve_sylvester(A, B, -C) Pn.append(P) pr += np.sum(inis * P) res.append(pr) Ret.append(np.array(res)) argIx += 1 elif type(argv[argIx]) is str and argv[argIx] == "ncMoms": # MOMENTS OF THE NUMBER OF JOBS # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ numOfQLMoms = argv[argIx + 1] # first calculate it at departure instants QLDPn = [Psis] dqlMoms = [] for n in range(1, numOfQLMoms + 1): A = Qspp + Psis * Qsmp B = Qsmm + Qsmp * Psis C = n * QLDPn[n - 1] * D[k + 1] bino = 1 for i in range(1, n): bino = bino * (n - i + 1) / i C = C + bino * QLDPn[i] * Qsmp * QLDPn[n - i] P = la.solve_sylvester(A, B, -C) QLDPn.append(P) dqlMoms.append(np.sum(inis * P)) dqlMoms = MomsFromFactorialMoms(dqlMoms) # now calculate it at random time instance pi = CTMCSolve(sD) lambdak = np.sum(pi * D[k + 1]) QLPn = [pi] qlMoms = [] iTerm = (ml.ones((N, 1)) * pi - sD).I for n in range(1, numOfQLMoms + 1): sumP = np.sum(inis * QLDPn[n]) + n * ( inis * QLDPn[n - 1] - QLPn[n - 1] * D[k + 1] / lambdak) * iTerm * np.sum(D[k + 1], 1) P = sumP * pi + n * (QLPn[n - 1] * D[k + 1] - inis * QLDPn[n - 1] * lambdak) * iTerm QLPn.append(P) qlMoms.append(np.sum(P)) qlMoms = MomsFromFactorialMoms(qlMoms) Ret.append(qlMoms) argIx += 1 elif type(argv[argIx]) is str and argv[argIx] == "ncDistr": # DISTRIBUTION OF THE NUMBER OF JOBS # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ numOfQLProbs = argv[argIx + 1] sDk = ml.matrix(D0) for i in range(k): sDk += D[i + 1] # first calculate it at departure instants Psid = FluidFundamentalMatrices(Qspp, Qspm, Qsmp, sDk, 'P', precision) Pn = [Psid] dqlProbs = inis * Psid for n in range(1, numOfQLProbs): A = Qspp + Psid * Qsmp B = sDk + Qsmp * Psid C = Pn[n - 1] * D[k + 1] for i in range(1, n): C += Pn[i] * Qsmp * Pn[n - i] P = la.solve_sylvester(A, B, -C) Pn.append(P) dqlProbs = ml.vstack((dqlProbs, inis * P)) # now calculate it at random time instance pi = CTMCSolve(sD) lambdak = np.sum(pi * D[k + 1]) iTerm = -(sD - D[k + 1]).I qlProbs = lambdak * dqlProbs[0, :] * iTerm for n in range(1, numOfQLProbs): P = (qlProbs[n - 1, :] * D[k + 1] + lambdak * (dqlProbs[n, :] - dqlProbs[n - 1, :])) * iTerm qlProbs = ml.vstack((qlProbs, P)) qlProbs = np.sum(qlProbs, 1).A.flatten() Ret.append(qlProbs) argIx += 1 else: raise Exception("MMAPPH1PRPR: Unknown parameter " + str(argv[argIx])) argIx += 1 elif k == K - 1: # step 3. calculate the performance measures # ========================================== argIx = 0 while argIx < len(argv): if argIx in eaten: argIx += 1 continue elif type(argv[argIx]) is str and argv[argIx] == "stMoms": # MOMENTS OF THE SOJOURN TIME # ~~~~~~~~~~~~~~~~~~~~~~~~~~~ numOfSTMoms = argv[argIx + 1] rtMoms = [] for i in range(1, numOfSTMoms + 1): rtMoms.append( np.sum( math.factorial(i) * kappa * (-Kw).I**(i + 1) * Bw)) Ret.append(rtMoms) argIx += 1 elif type(argv[argIx]) is str and argv[argIx] == "stDistr": # DISTRIBUTION OF THE SOJOURN TIME # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ stCdfPoints = argv[argIx + 1] rtDistr = [] for t in stCdfPoints: rtDistr.append( np.sum(kappa * (-Kw).I * (ml.eye(Kw.shape[0]) - la.expm(Kw * t)) * Bw)) Ret.append(np.array(rtDistr)) argIx += 1 elif type(argv[argIx]) is str and (argv[argIx] == "ncMoms" or argv[argIx] == "ncDistr"): L = np.kron(sD - D[k + 1], ml.eye(M[k])) + np.kron( ml.eye(N), S[k]) B = np.kron(ml.eye(N), s[k] * sigma[k]) F = np.kron(D[k + 1], ml.eye(M[k])) L0 = np.kron(sD - D[k + 1], ml.eye(M[k])) R = QBDFundamentalMatrices(B, L, F, 'R', precision) p0 = CTMCSolve(L0 + R * B) p0 = p0 / np.sum(p0 * (ml.eye(R.shape[0]) - R).I) if argv[argIx] == "ncMoms": # MOMENTS OF THE NUMBER OF JOBS # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ numOfQLMoms = argv[argIx + 1] qlMoms = [] for i in range(1, numOfQLMoms + 1): qlMoms.append( np.sum( math.factorial(i) * p0 * R**i * (ml.eye(R.shape[0]) - R).I**(i + 1))) Ret.append(MomsFromFactorialMoms(qlMoms)) elif argv[argIx] == "ncDistr": # DISTRIBUTION OF THE NUMBER OF JOBS # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ numOfQLProbs = argv[argIx + 1] qlProbs = [np.sum(p0)] for i in range(1, numOfQLProbs): qlProbs.append(np.sum(p0 * R**i)) Ret.append(np.array(qlProbs)) argIx += 1 else: raise Exception("MMAPPH1PRPR: Unknown parameter " + str(argv[argIx])) argIx += 1 if len(Ret) == 1: return Ret[0] else: return Ret
def SamplesFromMMAP(D, k, initial=None, prec=1e-14): """ Generates random samples from a marked Markovian arrival process. Parameters ---------- D : list of matrices of shape(M,M), length(N) The D0...DN matrices of the MMAP K : integer The number of samples to generate. prec : double, optional Numerical precision to check if the input MMAP is valid. The default value is 1e-14. Returns ------- x : matrix, shape(K,2) The random samples. Each row consists of two columns: the inter-arrival time and the type of the arrival. """ if butools.checkInput and not CheckMMAPRepresentation(D): raise Exception( "SamplesFromMMAP: Input is not a valid MMAP representation!") N = D[0].shape[0] if initial == None: # draw initial state according to the stationary distribution stst = CTMCSolve(SumMatrixList(D)).A.flatten() cummInitial = np.cumsum(stst) r = rand() state = 0 while cummInitial[state] <= r: state += 1 else: state = initial # auxilary variables sojourn = -1.0 / np.diag(D[0]) nextpr = ml.matrix(np.diag(sojourn)) * D[0] nextpr = nextpr - ml.matrix(np.diag(np.diag(nextpr))) for i in range(1, len(D)): nextpr = np.hstack((nextpr, np.diag(sojourn) * D[i])) nextpr = np.cumsum(nextpr, 1) if len(D) > 2: x = np.empty((k, 2)) else: x = np.empty(k) for n in range(k): time = 0 # play state transitions while state < N: time -= np.log(rand()) * sojourn[state] r = rand() nstate = 0 while nextpr[state, nstate] <= r: nstate += 1 state = nstate if len(D) > 2: x[n, 0] = time x[n, 1] = state // N else: x[n] = time state = state % N return x
def ImageFromMMAP(D, outFileName="display", prec=1e-13): """ Depicts the given marked Markovian arrival process, and either displays it or saves it to file. Parameters ---------- D : list of matrices of shape(M,M), length(N) The D0...DN matrices of the MMAP outFileName : string, optional If it is not provided, or equals to 'display', the image is displayed on the screen, otherwise it is written to the file. The file format is deduced from the file name. prec : double, optional Transition rates less then prec are considered to be zero and are left out from the image. The default value is 1e-13. Notes ----- The 'graphviz' software must be installed and available in the path to use this feature. """ if butools.checkInput and not CheckMMAPRepresentation(D): raise Exception( "ImageFromMMAP: Input is not a valid MMAP representation!") if outFileName == "display": outputFile = ".result.png" displ = True else: outputFile = outFileName displ = False inputFile = "_temp.dot" f = open(inputFile, "w") f.write("digraph G {\n") f.write("\trankdir=LR;\n") f.write('\tnode [shape=circle,width=0.3,height=0.3,label=""];\n') N = D[0].shape[0] # transitions without arrivals Dx = D[0] for i in range(N): for j in range(N): if i != j and abs(Dx[i, j]) > prec: f.write('\tn{0} -> n{1} [label="{2}"];\n'.format( i, j, Dx[i, j])) # transitions with arrivals for k in range(1, len(D)): Dx = D[k] for i in range(N): for j in range(N): if abs(Dx[i, j]) > prec: if len(D) == 2: f.write( '\tn{0} -> n{1} [style="dashed",label="{2}"];\n'. format(i, j, Dx[i, j])) else: f.write( '\tn{0} -> n{1} [style="solid",fontcolor="/dark28/{2}",color="/dark28/{3}",label="{4}"];\n' .format(i, j, min(k - 1, 8), min(k - 1, 8), Dx[i, j])) f.write('}\n') f.close() ext = os.path.splitext(outputFile)[1] call(['dot', '-T' + ext[1:], "_temp.dot", '-o', outputFile]) os.remove(inputFile) if displ: from IPython.display import Image i = Image(filename=outputFile) os.remove(outputFile) return i