1function result = qsys_mapg1k(D0, D1, svc, K, varargin)
2% RESULT = QSYS_MAPG1K(D0, D1, SVC, K)
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).
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.
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
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.
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]
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)
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
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.
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
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.
78% See also QSYS_MAPG1K_PERFLOW, QSYS_MAPG1, QSYS_MG1K_LOSS, QSYS_MMCK.
81addParameter(p, 'tol
', 1e-12);
82addParameter(p, 'nmax
', 200000);
85nmaxCap = p.Results.nmax;
88if size(D0, 2) ~= M || any(size(D1) ~= [M M])
89 line_error(mfilename, 'D0 and D1 must be square matrices of equal size.
');
91if K < 1 || K ~= round(K)
92 line_error(mfilename, 'Buffer size K must be a positive integer.
');
96 line_error(mfilename, 'D0 must have strictly negative diagonal entries.
');
99% Uniformization constant: theta >= max_i beta_i keeps I+D0/theta substochastic
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);
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;
114% A_m and Q_m for m = 0..K-1, plus B0 = sum_m A_m = E[exp((D0+D1)*S)]
116A = zeros(M, M, mmax+1);
117Q = zeros(M, M, mmax+1);
118Sn = zeros(M, M, mmax+1);
123Pt0 = eye(M) + D0/theta;
125PD = eye(M) + (D0+D1)/theta;
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);
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
134 Snew = zeros(M, M, mmax+1);
135 for m = 0:min(n+1, mmax)
138 acc = acc + Sn(:,:,m+1)*Pt0;
140 if m >= 1 && m-1 <= n
141 acc = acc + Sn(:,:,m)*Pt1;
152Psi = negD0inv*D1; % phase at the arrival that ends an idle period
153idle = negD0inv*e; % expected idle time from each phase
155% Embedded chain at departure epochs, state (n,j) -> index n*M+j
157lastblk = (K-1)*M + (1:M);
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);
165 % Bacc = sum_{m>=K-n} A_m: every further arrival overflows the buffer
166 P(rows, lastblk) = P(rows, lastblk) + Bacc;
171 P(rows, m*M + (1:M)) = P(rows, m*M + (1:M)) + Psi*A(:,:,m+1);
172 Bacc = Bacc - A(:,:,m+1);
174P(rows, lastblk) = P(rows, lastblk) + Psi*Bacc;
176rowdev = max(abs(sum(P, 2) - 1));
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));
182sigma = dtmc_solve(P);
186% Markov renewal reward over
the interval between successive departures
187idleTime = sigma0*idle;
188Ecyc = Smean + idleTime;
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);
203 acc = acc + Q(:,:,m+1);
206timeKvec = zeros(1, M);
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));
212 timeKvec = timeKvec + sigma0*Psi*(Qtot - Qcum(:,:,K-1));
214 timeKvec = timeKvec + sigma0*Psi*Qtot;
216pKvec = timeKvec/Ecyc;
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
224timeL = zeros(1, K+1);
227 sn_row = sigma(n*M + (1:M));
229 timeL(l+1) = timeL(l+1) + sn_row*Q(:,:,l-n+1)*e;
234 timeL(l+1) = timeL(l+1) + s0Psi*Q(:,:,l)*e;
236timeL(K+1) = sum(timeKvec);
238massdev = abs(sum(plevel) - 1);
240 line_error(mfilename, sprintf(['Level distribution has mass %.12f. The Q_m
' ...
241 'series has not converged; raise
''nmax
''.
'], sum(plevel)));
243meanQ = (0:K)*plevel(:);
244% Time at level 0 is the idle period alone, whose phase law is (-D0)^{-1}
245p0vec = (sigma0*negD0inv)/Ecyc;
247lambda = map_lambda({D0, D1});
252result.throughput = T;
253result.lossProbability = 1 - T/lambda;
254result.lambda = lambda;
255result.meanServiceTime = Smean;
256result.utilization = 1 - p0;
257result.rho = lambda*Smean;
262result.plevel = plevel;
263result.meanQueueLength = meanQ;
264result.analyzer = 'qsys_mapg1k
';
267% -------------------------------------------------------------------------
268function Smean = i_svcmean(svc)
269% Mean service time S of the descriptor.
270switch lower(svc.type)
272 Smean = svc.alpha*svc.theta;
276 Smean = -svc.alpha(:).'*(svc.T\ones(size(svc.T,1),1));
278 if isfield(svc,
'tmax'); tmax = svc.tmax;
else; tmax = Inf; end
279 Smean = i_dquad(@(x) x, svc.pdf, tmax);
281 line_error(mfilename, sprintf(
'Unsupported service type ''%s''.', svc.type));
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.');
293Smean = i_svcmean(svc);
294switch lower(svc.type)
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));
301 fn = @(nn) exp(-theta*d + nn*log(theta*d) - gammaln(nn+1));
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);
309 if isfield(svc,
'tmax'); tmax = svc.tmax;
else; tmax = Inf; end
310 fn = @(nn) i_cquad(svc.pdf, theta, nn, tmax);
312 line_error(mfilename, sprintf(
'Unsupported service type ''%s''.', svc.type));
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))));
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.
329 if abs(1 - sum(cn)) <= tol
333 add = fn((n:min(cap-1, n + 63)).');
335 if sum(add) <= eps*sum(cn)
342% -------------------------------------------------------------------------
343function v = i_phblk(nn, alv, Minv, tv, theta)
344v = zeros(numel(nn), 1);
346 v(i) = theta^nn(i)*(alv*(Minv^(nn(i)+1))*tv);
350% -------------------------------------------------------------------------
351function v = i_cquad(pdf, theta, nn, tmax)
352v = zeros(numel(nn), 1);
355 v(i) = i_dquad(@(x) exp(-theta*x + n*log(theta*x) - gammaln(n+1)), pdf, tmax);
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.
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.
379v = integral(@(u) i_finite(w(exp(u)).*pdf(exp(u)).*exp(u)), -Inf, ulim, ...
380 'AbsTol', 1e-300,
'RelTol', 1e-13);
383% -------------------------------------------------------------------------
384function y = i_finite(y)
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)));