LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
qsys_mapg1k.m
1function result = qsys_mapg1k(D0, D1, svc, K, varargin)
2% RESULT = QSYS_MAPG1K(D0, D1, SVC, K)
3%
4% Exact analysis of a MAP/G/1/K queue with tail drop: Markovian arrivals,
5% arbitrary service time distribution F, and a finite buffer of K packets
6% (the position held by the packet in transmission included).
7%
8% Unlike QSYS_MAPG1, the service time is NOT fitted to a phase-type
9% distribution: F enters exactly, through the functionals A_m and Q_m
10% evaluated by uniformization of the arrival MAP. Unlike QSYS_MG1K_LOSS,
11% which embeds the same way but assumes Poisson input, arrivals may be a
12% general MAP, so flows with equal rate but different interarrival
13% variability or autocorrelation are told apart.
14%
15% D0, D1 - MAP parameter matrices (M x M), D0 + D1 an irreducible generator
16% SVC - service time descriptor, a struct with field 'type':
17% 'gamma' : fields alpha (shape), theta (scale). Covers Exp
18% (alpha=1) and Erlang (alpha integer).
19% 'det' : field d (constant service time)
20% 'ph' : fields alpha (1 x p row), T (p x p subgenerator)
21% 'density' : field pdf (handle), optional field tmax
22% K - buffer size in packets, K >= 1
23%
24% RESULT = QSYS_MAPG1K(..., 'tol', TOL) sets the uniformization truncation
25% tolerance (default 1e-12). RESULT = QSYS_MAPG1K(..., 'nmax', N) caps the
26% uniformization order.
27%
28% Returns a struct with fields:
29% p0 - stationary probability of an empty buffer
30% pK - stationary probability of a full buffer
31% lossProbability - loss ratio of the aggregate arrival stream, 1-T/lambda
32% throughput - aggregate throughput [pkts/s]
33% lambda - aggregate arrival rate of the MAP
34% meanServiceTime - S = E[service time]
35% utilization - 1 - p0
36% rho - offered load lambda*S
37% nmax - uniformization order used
38% sigma - stationary law of the embedded chain, K*M entries
39% pKvec - 1 x M, P(buffer full, phase j), summing to pK
40% p0vec - 1 x M, P(buffer empty, phase j), summing to p0
41% plevel - 1 x (K+1), time-stationary P(level = l), l = 0..K
42% meanQueueLength - E[number in system], sum_l l*plevel(l+1)
43%
44% Method. The chain embedded at departure epochs is used, in the state
45% (n,j): n = 0..K-1 packets left behind by a departure, j = MAP phase. With
46% A_m the matrix of "m arrivals during a service, phase i -> j",
47% n >= 1: n' = n-1+min(m, K-n), via A_m, overflow sum_{m>=K-n} A_m
48% n == 0: the phase first jumps by (-D0)^{-1}*D1 (the idle period ends at
49% an arrival), and the service then proceeds as from n = 1.
50% Its stationary law sigma gives, by Markov renewal reward,
51% E[cycle] = S + sum_j sigma(0,j)*idle_j, idle = (-D0)^{-1}*e
52% T = 1/E[cycle], p0 = (sum_j sigma(0,j)*idle_j)/E[cycle] = 1 - T*S
53% pK = E[time at level K per cycle]/E[cycle], from Q_m,
54% where Q_m is the expected time within a service with exactly m arrivals so
55% far. Time-stationary p0 and pK follow, so no PASTA assumption is needed on
56% the MAP side.
57%
58% This is not the transform solution of Theorem 1 of [1], which is stated in
59% terms of a sequence R_m obeying R(z) = z*(A(z)-z*I)^{-1}. That sequence
60% grows geometrically, at a rate set by the smallest zero of det(A(z)-z*I),
61% while the quantity extracted from it stays O(s) as s -> 0+; cond(G(s))
62% therefore grows like that ratio^K and crosses the double-precision ceiling
63% near K = 20 for the flows of [1] under gamma service with CV = 2, and near
64% K = 10 under constant service, where A_0 = exp(D0*d) has entries O(1e-8).
65% Reference [1] evaluates its formulae in arbitrary precision, so the
66% restriction is invisible there. The embedded chain used here has every
67% entry a probability or a time and is stable for any K and any F.
68%
69% TEST (M/M/1/5, exact loss 0.04812030):
70% r=qsys_mapg1k(-2,2,struct('type','gamma','alpha',1,'theta',1/3),5); r.pK
71%
72% References:
73% [1] Chydzinski, A. Per-Flow Throughput of a FIFO Buffer. Applied System
74% Innovation 2026, 9, 112.
75% [2] Niu, Z.; Cooper, R.B. Transform-Free Analysis of M/G/1/K and Related
76% Queues. Mathematics of Operations Research 1993, 18, 486-510.
77%
78% See also QSYS_MAPG1K_PERFLOW, QSYS_MAPG1, QSYS_MG1K_LOSS, QSYS_MMCK.
79
80p = inputParser;
81addParameter(p, 'tol', 1e-12);
82addParameter(p, 'nmax', 200000);
83parse(p, varargin{:});
84tol = p.Results.tol;
85nmaxCap = p.Results.nmax;
86
87M = size(D0, 1);
88if size(D0, 2) ~= M || any(size(D1) ~= [M M])
89 line_error(mfilename, 'D0 and D1 must be square matrices of equal size.');
90end
91if K < 1 || K ~= round(K)
92 line_error(mfilename, 'Buffer size K must be a positive integer.');
93end
94beta = -diag(D0);
95if any(beta <= 0)
96 line_error(mfilename, 'D0 must have strictly negative diagonal entries.');
97end
98
99% Uniformization constant: theta >= max_i beta_i keeps I+D0/theta substochastic
100theta = max(beta);
101
102% c_n = E[exp(-theta*S)*(theta*S)^n/n!], summing to f(0) = 1
103[cn, Smean] = i_service(svc, theta, tol, nmaxCap);
104nmax = numel(cn) - 1;
105
106% d_n = int_0^inf exp(-theta*x)*(theta*x)^n/n!*(1-F(x))dx. The identity
107% -theta*d_n + theta*d_{n-1} = c_n - delta_{n,0} telescopes at s = 0 to
108% d_n = (1 - sum_{k<=n} c_k)/theta, which cancels as the sum approaches 1;
109% the equivalent tail form below adds only nonnegative terms. It certifies
110% itself through sum_n d_n = sum_k k*c_k/theta = E[S] = S.
111tailc = flipud(cumsum(flipud(cn(:))));
112dn = [tailc(2:end); 0]/theta;
113
114% A_m and Q_m for m = 0..K-1, plus B0 = sum_m A_m = E[exp((D0+D1)*S)]
115mmax = max(K-1, 0);
116A = zeros(M, M, mmax+1);
117Q = zeros(M, M, mmax+1);
118Sn = zeros(M, M, mmax+1);
119Sn(:,:,1) = eye(M);
120B0 = zeros(M);
121Qtot = zeros(M);
122Pn = eye(M);
123Pt0 = eye(M) + D0/theta;
124Pt1 = D1/theta;
125PD = eye(M) + (D0+D1)/theta;
126for n = 0:nmax
127 for m = 0:min(n, mmax)
128 A(:,:,m+1) = A(:,:,m+1) + Sn(:,:,m+1)*cn(n+1);
129 Q(:,:,m+1) = Q(:,:,m+1) + Sn(:,:,m+1)*dn(n+1);
130 end
131 B0 = B0 + Pn*cn(n+1);
132 Qtot = Qtot + Pn*dn(n+1); % sum_m Q_m = int_0^inf exp(D*x)*(1-F(x))dx
133 if n < nmax
134 Snew = zeros(M, M, mmax+1);
135 for m = 0:min(n+1, mmax)
136 acc = zeros(M);
137 if m <= n
138 acc = acc + Sn(:,:,m+1)*Pt0;
139 end
140 if m >= 1 && m-1 <= n
141 acc = acc + Sn(:,:,m)*Pt1;
142 end
143 Snew(:,:,m+1) = acc;
144 end
145 Sn = Snew;
146 Pn = Pn*PD;
147 end
148end
149
150e = ones(M, 1);
151negD0inv = inv(-D0);
152Psi = negD0inv*D1; % phase at the arrival that ends an idle period
153idle = negD0inv*e; % expected idle time from each phase
154
155% Embedded chain at departure epochs, state (n,j) -> index n*M+j
156P = zeros(K*M, K*M);
157lastblk = (K-1)*M + (1:M);
158for n = 1:K-1
159 rows = n*M + (1:M);
160 Bacc = B0;
161 for m = 0:K-n-1
162 P(rows, (n-1+m)*M + (1:M)) = P(rows, (n-1+m)*M + (1:M)) + A(:,:,m+1);
163 Bacc = Bacc - A(:,:,m+1);
164 end
165 % Bacc = sum_{m>=K-n} A_m: every further arrival overflows the buffer
166 P(rows, lastblk) = P(rows, lastblk) + Bacc;
167end
168rows = 1:M;
169Bacc = B0;
170for m = 0:K-2
171 P(rows, m*M + (1:M)) = P(rows, m*M + (1:M)) + Psi*A(:,:,m+1);
172 Bacc = Bacc - A(:,:,m+1);
173end
174P(rows, lastblk) = P(rows, lastblk) + Psi*Bacc;
175
176rowdev = max(abs(sum(P, 2) - 1));
177if rowdev > 1e-8
178 line_error(mfilename, sprintf(['Embedded chain rows deviate from 1 by %.2e. ' ...
179 'The uniformization series for A_m has not converged; raise ''nmax''.'], rowdev));
180end
181
182sigma = dtmc_solve(P);
183sigma = sigma(:).';
184sigma0 = sigma(1:M);
185
186% Markov renewal reward over the interval between successive departures
187idleTime = sigma0*idle;
188Ecyc = Smean + idleTime;
189T = 1/Ecyc;
190p0 = idleTime/Ecyc;
191
192% Expected time spent with the buffer full during a cycle. From n >= 1 the
193% level is n plus the arrivals accepted so far, so it stands at K once K-n
194% have arrived, for an expected sum_{m>=K-n} Q_m*e of the service. From
195% n = 0 the same holds with n = 1 after the phase jump through Psi.
196% Resolved by phase, not merely summed: an MMAP's class-k arrivals leave
197% phase i at rate (D1k*e)_i, so the per-class blocking probability needs the
198% joint law of (level, phase) at level K, not the scalar pK. See
199% QSYS_MMAPG1K, which reads pKvec to get exact per-class loss ratios.
200Qcum = zeros(M, M, mmax+1);
201acc = zeros(M);
202for m = 0:mmax
203 acc = acc + Q(:,:,m+1);
204 Qcum(:,:,m+1) = acc;
205end
206timeKvec = zeros(1, M);
207for n = 1:K-1
208 r = K-n-1; % Qcum(:,:,r+1) = sum_{m=0}^{r} Q_m
209 timeKvec = timeKvec + sigma(n*M + (1:M))*(Qtot - Qcum(:,:,r+1));
210end
211if K >= 2
212 timeKvec = timeKvec + sigma0*Psi*(Qtot - Qcum(:,:,K-1));
213else
214 timeKvec = timeKvec + sigma0*Psi*Qtot;
215end
216pKvec = timeKvec/Ecyc;
217pK = sum(pKvec);
218
219% The same Q_m give the expected time at every level, not just at K: from a
220% departure leaving n >= 1 the level stands at n+m while m arrivals have
221% been accepted, so level l is held for Q_{l-n}*e; from n = 0 the level
222% starts at 1 after the phase jump through Psi, so level l corresponds to
223% m = l-1 arrivals.
224timeL = zeros(1, K+1);
225timeL(1) = idleTime;
226for n = 1:K-1
227 sn_row = sigma(n*M + (1:M));
228 for l = n:K-1
229 timeL(l+1) = timeL(l+1) + sn_row*Q(:,:,l-n+1)*e;
230 end
231end
232s0Psi = sigma0*Psi;
233for l = 1:K-1
234 timeL(l+1) = timeL(l+1) + s0Psi*Q(:,:,l)*e;
235end
236timeL(K+1) = sum(timeKvec);
237plevel = timeL/Ecyc;
238massdev = abs(sum(plevel) - 1);
239if massdev > 1e-8
240 line_error(mfilename, sprintf(['Level distribution has mass %.12f. The Q_m ' ...
241 'series has not converged; raise ''nmax''.'], sum(plevel)));
242end
243meanQ = (0:K)*plevel(:);
244% Time at level 0 is the idle period alone, whose phase law is (-D0)^{-1}
245p0vec = (sigma0*negD0inv)/Ecyc;
246
247lambda = map_lambda({D0, D1});
248
249result = struct();
250result.p0 = p0;
251result.pK = pK;
252result.throughput = T;
253result.lossProbability = 1 - T/lambda;
254result.lambda = lambda;
255result.meanServiceTime = Smean;
256result.utilization = 1 - p0;
257result.rho = lambda*Smean;
258result.nmax = nmax;
259result.sigma = sigma;
260result.pKvec = pKvec;
261result.p0vec = p0vec;
262result.plevel = plevel;
263result.meanQueueLength = meanQ;
264result.analyzer = 'qsys_mapg1k';
265end
266
267% -------------------------------------------------------------------------
268function Smean = i_svcmean(svc)
269% Mean service time S of the descriptor.
270switch lower(svc.type)
271 case 'gamma'
272 Smean = svc.alpha*svc.theta;
273 case 'det'
274 Smean = svc.d;
275 case 'ph'
276 Smean = -svc.alpha(:).'*(svc.T\ones(size(svc.T,1),1));
277 case 'density'
278 if isfield(svc, 'tmax'); tmax = svc.tmax; else; tmax = Inf; end
279 Smean = i_dquad(@(x) x, svc.pdf, tmax);
280 otherwise
281 line_error(mfilename, sprintf('Unsupported service type ''%s''.', svc.type));
282end
283end
284
285% -------------------------------------------------------------------------
286function [cn, Smean] = i_service(svc, theta, tol, nmaxCap)
287% c_n = E[exp(-theta*S)*(theta*S)^n/n!] for n = 0..nmax, and the mean S.
288% sum_{n>=0} c_n = E[exp(-theta*S)*exp(theta*S)] = 1 exactly, which both
289% sets the truncation order and certifies it.
290if ~isfield(svc, 'type')
291 line_error(mfilename, 'Service descriptor must have a ''type'' field.');
292end
293Smean = i_svcmean(svc);
294switch lower(svc.type)
295 case 'gamma'
296 al = svc.alpha; th = svc.theta;
297 fn = @(nn) exp(nn*log(theta*th) - gammaln(nn+1) + gammaln(al+nn) ...
298 - gammaln(al) - (al+nn)*log1p(th*theta));
299 case 'det'
300 d = svc.d;
301 fn = @(nn) exp(-theta*d + nn*log(theta*d) - gammaln(nn+1));
302 case 'ph'
303 alv = svc.alpha(:).'; T = svc.T;
304 tv = -T*ones(size(T,1), 1);
305 % c_n = theta^n * alpha * (theta*I-T)^{-(n+1)} * t
306 Minv = inv(theta*eye(size(T)) - T);
307 fn = @(nn) i_phblk(nn, alv, Minv, tv, theta);
308 case 'density'
309 if isfield(svc, 'tmax'); tmax = svc.tmax; else; tmax = Inf; end
310 fn = @(nn) i_cquad(svc.pdf, theta, nn, tmax);
311 otherwise
312 line_error(mfilename, sprintf('Unsupported service type ''%s''.', svc.type));
313end
314cn = i_grow(fn, tol, nmaxCap, i_guess(theta*Smean, nmaxCap));
315if abs(1 - sum(cn)) > 1e-6
316 line_warning(mfilename, sprintf(['Uniformization series truncated at n=%d with ' ...
317 'residual %g; increase ''nmax''.'], numel(cn)-1, abs(1 - sum(cn))));
318end
319end
320
321% -------------------------------------------------------------------------
322function cn = i_grow(fn, tol, cap, n0)
323% Build c_0..c_N in blocks, stopping when the series sums to 1 within tol or
324% when a whole block adds nothing in floating point, i.e. the representable
325% series is exhausted. The second criterion terminates paths whose terms are
326% known only to quadrature accuracy, where the first can never be met.
327cn = fn((0:n0).');
328while numel(cn) < cap
329 if abs(1 - sum(cn)) <= tol
330 break
331 end
332 n = numel(cn);
333 add = fn((n:min(cap-1, n + 63)).');
334 cn = [cn; add];
335 if sum(add) <= eps*sum(cn)
336 break
337 end
338end
339cn = cn(:);
340end
341
342% -------------------------------------------------------------------------
343function v = i_phblk(nn, alv, Minv, tv, theta)
344v = zeros(numel(nn), 1);
345for i = 1:numel(nn)
346 v(i) = theta^nn(i)*(alv*(Minv^(nn(i)+1))*tv);
347end
348end
349
350% -------------------------------------------------------------------------
351function v = i_cquad(pdf, theta, nn, tmax)
352v = zeros(numel(nn), 1);
353for i = 1:numel(nn)
354 n = nn(i);
355 v(i) = i_dquad(@(x) exp(-theta*x + n*log(theta*x) - gammaln(n+1)), pdf, tmax);
356end
357end
358
359% -------------------------------------------------------------------------
360function v = i_dquad(w, pdf, tmax)
361% E[w(S)] for a service law given by a density, under the substitution
362% x = exp(u). An integrable density may diverge at the origin (the gamma
363% density behaves as x^(alpha-1), i.e. x^(-0.75) at the CV=2 shape used in
364% [1]), which caps adaptive quadrature on [0,tmax] at a few digits. The
365% Jacobian exp(u) turns x^(alpha-1)dx into exp(alpha*u)du, which decays
366% smoothly as u -> -Inf for any alpha > 0, so the singularity disappears
367% rather than being resolved.
368%
369% The transformed integrand tends to 0 at both ends: w is bounded and
370% integrability of pdf forces x*pdf(x) -> 0 as x -> 0 and as x -> Inf. In
371% floating point those limits are reached as 0*Inf, since exp(u) underflows
372% while pdf(exp(u)) overflows, so the NaN produced there is an artifact of
373% the substitution and is replaced by the analytic limit.
374if isfinite(tmax)
375 ulim = log(tmax);
376else
377 ulim = Inf;
378end
379v = integral(@(u) i_finite(w(exp(u)).*pdf(exp(u)).*exp(u)), -Inf, ulim, ...
380 'AbsTol', 1e-300, 'RelTol', 1e-13);
381end
382
383% -------------------------------------------------------------------------
384function y = i_finite(y)
385y(~isfinite(y)) = 0;
386end
387
388% -------------------------------------------------------------------------
389function n = i_guess(m, cap)
390% Initial uniformization order: mean plus a generous deviation allowance.
391n = min(cap, max(32, ceil(m + 10*sqrt(max(m, 1)) + 32)));
392end
Definition Station.m:245