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% see _kb/03-api-layer.md (qsys/ family) for rationale
107tailc = flipud(cumsum(flipud(cn(:))));
108dn = [tailc(2:end); 0]/theta;
110% A_m and Q_m for m = 0..K-1, plus B0 = sum_m A_m = E[exp((D0+D1)*S)]
112A = zeros(M, M, mmax+1);
113Q = zeros(M, M, mmax+1);
114Sn = zeros(M, M, mmax+1);
119Pt0 = eye(M) + D0/theta;
121PD = eye(M) + (D0+D1)/theta;
123 for m = 0:min(n, mmax)
124 A(:,:,m+1) = A(:,:,m+1) + Sn(:,:,m+1)*cn(n+1);
125 Q(:,:,m+1) = Q(:,:,m+1) + Sn(:,:,m+1)*dn(n+1);
127 B0 = B0 + Pn*cn(n+1);
128 Qtot = Qtot + Pn*dn(n+1); % sum_m Q_m = int_0^inf exp(D*x)*(1-F(x))dx
130 Snew = zeros(M, M, mmax+1);
131 for m = 0:min(n+1, mmax)
134 acc = acc + Sn(:,:,m+1)*Pt0;
136 if m >= 1 && m-1 <= n
137 acc = acc + Sn(:,:,m)*Pt1;
148Psi = negD0inv*D1; % phase at the arrival that ends an idle period
149idle = negD0inv*e; % expected idle time from each phase
151% Embedded chain at departure epochs, state (n,j) -> index n*M+j
153lastblk = (K-1)*M + (1:M);
158 P(rows, (n-1+m)*M + (1:M)) = P(rows, (n-1+m)*M + (1:M)) + A(:,:,m+1);
159 Bacc = Bacc - A(:,:,m+1);
161 % Bacc = sum_{m>=K-n} A_m: every further arrival overflows the buffer
162 P(rows, lastblk) = P(rows, lastblk) + Bacc;
167 P(rows, m*M + (1:M)) = P(rows, m*M + (1:M)) + Psi*A(:,:,m+1);
168 Bacc = Bacc - A(:,:,m+1);
170P(rows, lastblk) = P(rows, lastblk) + Psi*Bacc;
172rowdev = max(abs(sum(P, 2) - 1));
174 line_error(mfilename, sprintf(['Embedded chain rows deviate from 1 by %.2e.
' ...
175 'The uniformization series for A_m has not converged; raise
''nmax
''.
'], rowdev));
178sigma = dtmc_solve(P);
182% Markov renewal reward over the interval between successive departures
183idleTime = sigma0*idle;
184Ecyc = Smean + idleTime;
188% see _kb/03-api-layer.md (qsys/ family)
for rationale
189Qcum = zeros(M, M, mmax+1);
192 acc = acc + Q(:,:,m+1);
195timeKvec = zeros(1, M);
197 r = K-n-1; % Qcum(:,:,r+1) =
sum_{m=0}^{r} Q_m
198 timeKvec = timeKvec + sigma(n*M + (1:M))*(Qtot - Qcum(:,:,r+1));
201 timeKvec = timeKvec + sigma0*Psi*(Qtot - Qcum(:,:,K-1));
203 timeKvec = timeKvec + sigma0*Psi*Qtot;
205pKvec = timeKvec/Ecyc;
208% see _kb/03-api-layer.md (qsys/ family)
for rationale
209timeL = zeros(1, K+1);
212 sn_row = sigma(n*M + (1:M));
214 timeL(l+1) = timeL(l+1) + sn_row*Q(:,:,l-n+1)*e;
219 timeL(l+1) = timeL(l+1) + s0Psi*Q(:,:,l)*e;
221timeL(K+1) = sum(timeKvec);
223massdev = abs(sum(plevel) - 1);
225 line_error(mfilename, sprintf([
'Level distribution has mass %.12f. The Q_m ' ...
226 'series has not converged; raise ''nmax''.'], sum(plevel)));
228meanQ = (0:K)*plevel(:);
229% Time at level 0
is the idle period alone, whose phase law
is (-D0)^{-1}
230p0vec = (sigma0*negD0inv)/Ecyc;
232lambda = map_lambda({D0, D1});
237result.throughput = T;
238result.lossProbability = 1 - T/lambda;
239result.lambda = lambda;
240result.meanServiceTime = Smean;
241result.utilization = 1 - p0;
242result.rho = lambda*Smean;
247result.plevel = plevel;
248result.meanQueueLength = meanQ;
249result.analyzer =
'qsys_mapg1k';
252% -------------------------------------------------------------------------
253function Smean = i_svcmean(svc)
254% Mean service time S of the descriptor.
255switch lower(svc.type)
257 Smean = svc.alpha*svc.theta;
261 Smean = -svc.alpha(:).'*(svc.T\ones(size(svc.T,1),1));
263 if isfield(svc, 'tmax'); tmax = svc.tmax; else; tmax = Inf; end
264 Smean = i_dquad(@(x) x, svc.pdf, tmax);
266 line_error(mfilename, sprintf('Unsupported service type ''%s''.', svc.type));
270% -------------------------------------------------------------------------
271function [cn, Smean] = i_service(svc, theta, tol, nmaxCap)
272% c_n = E[exp(-theta*S)*(theta*S)^n/n!] for n = 0..nmax, and the mean S.
273%
sum_{n>=0} c_n = E[exp(-theta*S)*exp(theta*S)] = 1 exactly, which both
274% sets the truncation order and certifies it.
275if ~isfield(svc,
'type')
276 line_error(mfilename, 'Service descriptor must have a ''type'' field.');
278Smean = i_svcmean(svc);
279switch lower(svc.type)
281 al = svc.alpha; th = svc.theta;
282 fn = @(nn) exp(nn*log(theta*th) - gammaln(nn+1) + gammaln(al+nn) ...
283 - gammaln(al) - (al+nn)*log1p(th*theta));
286 fn = @(nn) exp(-theta*d + nn*log(theta*d) - gammaln(nn+1));
288 alv = svc.alpha(:).'; T = svc.T;
289 tv = -T*ones(size(T,1), 1);
290 % c_n = theta^n * alpha * (theta*I-T)^{-(n+1)} * t
291 Minv = inv(theta*eye(size(T)) - T);
292 fn = @(nn) i_phblk(nn, alv, Minv, tv, theta);
294 if isfield(svc,
'tmax'); tmax = svc.tmax;
else; tmax = Inf; end
295 fn = @(nn) i_cquad(svc.pdf, theta, nn, tmax);
297 line_error(mfilename, sprintf(
'Unsupported service type ''%s''.', svc.type));
299cn = i_grow(fn, tol, nmaxCap, i_guess(theta*Smean, nmaxCap));
300if abs(1 - sum(cn)) > 1e-6
301 line_warning(mfilename, sprintf([
'Uniformization series truncated at n=%d with ' ...
302 'residual %g; increase ''nmax''.'], numel(cn)-1, abs(1 - sum(cn))));
306% -------------------------------------------------------------------------
307function cn = i_grow(fn, tol, cap, n0)
308% Build c_0..c_N in blocks, stopping when the series sums to 1 within tol or
309% when a whole block adds nothing in floating point, i.e. the representable
310% series
is exhausted. The second criterion terminates paths whose terms are
311% known only to quadrature accuracy, where the first can never be met.
314 if abs(1 - sum(cn)) <= tol
318 add = fn((n:min(cap-1, n + 63)).');
320 if sum(add) <= eps*sum(cn)
327% -------------------------------------------------------------------------
328function v = i_phblk(nn, alv, Minv, tv, theta)
329v = zeros(numel(nn), 1);
331 v(i) = theta^nn(i)*(alv*(Minv^(nn(i)+1))*tv);
335% -------------------------------------------------------------------------
336function v = i_cquad(pdf, theta, nn, tmax)
337v = zeros(numel(nn), 1);
340 v(i) = i_dquad(@(x) exp(-theta*x + n*log(theta*x) - gammaln(n+1)), pdf, tmax);
344% -------------------------------------------------------------------------
345function v = i_dquad(w, pdf, tmax)
346% E[w(S)]
for a service law given by a density, under the substitution
347% x = exp(u). An integrable density may diverge at the origin (the gamma
348% density behaves as x^(alpha-1), i.e. x^(-0.75) at the CV=2 shape used in
349% [1]), which caps adaptive quadrature on [0,tmax] at a few digits. The
350% Jacobian exp(u) turns x^(alpha-1)dx into exp(alpha*u)du, which decays
351% smoothly as u -> -Inf
for any alpha > 0, so the singularity disappears
352% rather than being resolved.
354% The transformed integrand tends to 0 at both ends: w
is bounded and
355% integrability of pdf forces x*pdf(x) -> 0 as x -> 0 and as x -> Inf. In
356% floating point those limits are reached as 0*Inf, since exp(u) underflows
357%
while pdf(exp(u)) overflows, so the NaN produced there
is an artifact of
358% the substitution and
is replaced by the analytic limit.
364v = integral(@(u) i_finite(w(exp(u)).*pdf(exp(u)).*exp(u)), -Inf, ulim, ...
365 'AbsTol', 1e-300,
'RelTol', 1e-13);
368% -------------------------------------------------------------------------
369function y = i_finite(y)
373% -------------------------------------------------------------------------
374function n = i_guess(m, cap)
375% Initial uniformization order: mean plus a generous deviation allowance.
376n = min(cap, max(32, ceil(m + 10*sqrt(max(m, 1)) + 32)));