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% see _kb/03-api-layer.md (qsys/ family) for rationale
107tailc = flipud(cumsum(flipud(cn(:))));
108dn = [tailc(2:end); 0]/theta;
109
110% A_m and Q_m for m = 0..K-1, plus B0 = sum_m A_m = E[exp((D0+D1)*S)]
111mmax = max(K-1, 0);
112A = zeros(M, M, mmax+1);
113Q = zeros(M, M, mmax+1);
114Sn = zeros(M, M, mmax+1);
115Sn(:,:,1) = eye(M);
116B0 = zeros(M);
117Qtot = zeros(M);
118Pn = eye(M);
119Pt0 = eye(M) + D0/theta;
120Pt1 = D1/theta;
121PD = eye(M) + (D0+D1)/theta;
122for n = 0:nmax
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);
126 end
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
129 if n < nmax
130 Snew = zeros(M, M, mmax+1);
131 for m = 0:min(n+1, mmax)
132 acc = zeros(M);
133 if m <= n
134 acc = acc + Sn(:,:,m+1)*Pt0;
135 end
136 if m >= 1 && m-1 <= n
137 acc = acc + Sn(:,:,m)*Pt1;
138 end
139 Snew(:,:,m+1) = acc;
140 end
141 Sn = Snew;
142 Pn = Pn*PD;
143 end
144end
145
146e = ones(M, 1);
147negD0inv = inv(-D0);
148Psi = negD0inv*D1; % phase at the arrival that ends an idle period
149idle = negD0inv*e; % expected idle time from each phase
150
151% Embedded chain at departure epochs, state (n,j) -> index n*M+j
152P = zeros(K*M, K*M);
153lastblk = (K-1)*M + (1:M);
154for n = 1:K-1
155 rows = n*M + (1:M);
156 Bacc = B0;
157 for m = 0:K-n-1
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);
160 end
161 % Bacc = sum_{m>=K-n} A_m: every further arrival overflows the buffer
162 P(rows, lastblk) = P(rows, lastblk) + Bacc;
163end
164rows = 1:M;
165Bacc = B0;
166for m = 0:K-2
167 P(rows, m*M + (1:M)) = P(rows, m*M + (1:M)) + Psi*A(:,:,m+1);
168 Bacc = Bacc - A(:,:,m+1);
169end
170P(rows, lastblk) = P(rows, lastblk) + Psi*Bacc;
171
172rowdev = max(abs(sum(P, 2) - 1));
173if rowdev > 1e-8
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));
176end
177
178sigma = dtmc_solve(P);
179sigma = sigma(:).';
180sigma0 = sigma(1:M);
181
182% Markov renewal reward over the interval between successive departures
183idleTime = sigma0*idle;
184Ecyc = Smean + idleTime;
185T = 1/Ecyc;
186p0 = idleTime/Ecyc;
187
188% see _kb/03-api-layer.md (qsys/ family) for rationale
189Qcum = zeros(M, M, mmax+1);
190acc = zeros(M);
191for m = 0:mmax
192 acc = acc + Q(:,:,m+1);
193 Qcum(:,:,m+1) = acc;
194end
195timeKvec = zeros(1, M);
196for n = 1:K-1
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));
199end
200if K >= 2
201 timeKvec = timeKvec + sigma0*Psi*(Qtot - Qcum(:,:,K-1));
202else
203 timeKvec = timeKvec + sigma0*Psi*Qtot;
204end
205pKvec = timeKvec/Ecyc;
206pK = sum(pKvec);
207
208% see _kb/03-api-layer.md (qsys/ family) for rationale
209timeL = zeros(1, K+1);
210timeL(1) = idleTime;
211for n = 1:K-1
212 sn_row = sigma(n*M + (1:M));
213 for l = n:K-1
214 timeL(l+1) = timeL(l+1) + sn_row*Q(:,:,l-n+1)*e;
215 end
216end
217s0Psi = sigma0*Psi;
218for l = 1:K-1
219 timeL(l+1) = timeL(l+1) + s0Psi*Q(:,:,l)*e;
220end
221timeL(K+1) = sum(timeKvec);
222plevel = timeL/Ecyc;
223massdev = abs(sum(plevel) - 1);
224if massdev > 1e-8
225 line_error(mfilename, sprintf(['Level distribution has mass %.12f. The Q_m ' ...
226 'series has not converged; raise ''nmax''.'], sum(plevel)));
227end
228meanQ = (0:K)*plevel(:);
229% Time at level 0 is the idle period alone, whose phase law is (-D0)^{-1}
230p0vec = (sigma0*negD0inv)/Ecyc;
231
232lambda = map_lambda({D0, D1});
233
234result = struct();
235result.p0 = p0;
236result.pK = pK;
237result.throughput = T;
238result.lossProbability = 1 - T/lambda;
239result.lambda = lambda;
240result.meanServiceTime = Smean;
241result.utilization = 1 - p0;
242result.rho = lambda*Smean;
243result.nmax = nmax;
244result.sigma = sigma;
245result.pKvec = pKvec;
246result.p0vec = p0vec;
247result.plevel = plevel;
248result.meanQueueLength = meanQ;
249result.analyzer = 'qsys_mapg1k';
250end
251
252% -------------------------------------------------------------------------
253function Smean = i_svcmean(svc)
254% Mean service time S of the descriptor.
255switch lower(svc.type)
256 case 'gamma'
257 Smean = svc.alpha*svc.theta;
258 case 'det'
259 Smean = svc.d;
260 case 'ph'
261 Smean = -svc.alpha(:).'*(svc.T\ones(size(svc.T,1),1));
262 case 'density'
263 if isfield(svc, 'tmax'); tmax = svc.tmax; else; tmax = Inf; end
264 Smean = i_dquad(@(x) x, svc.pdf, tmax);
265 otherwise
266 line_error(mfilename, sprintf('Unsupported service type ''%s''.', svc.type));
267end
268end
269
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.');
277end
278Smean = i_svcmean(svc);
279switch lower(svc.type)
280 case 'gamma'
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));
284 case 'det'
285 d = svc.d;
286 fn = @(nn) exp(-theta*d + nn*log(theta*d) - gammaln(nn+1));
287 case 'ph'
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);
293 case 'density'
294 if isfield(svc, 'tmax'); tmax = svc.tmax; else; tmax = Inf; end
295 fn = @(nn) i_cquad(svc.pdf, theta, nn, tmax);
296 otherwise
297 line_error(mfilename, sprintf('Unsupported service type ''%s''.', svc.type));
298end
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))));
303end
304end
305
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.
312cn = fn((0:n0).');
313while numel(cn) < cap
314 if abs(1 - sum(cn)) <= tol
315 break
316 end
317 n = numel(cn);
318 add = fn((n:min(cap-1, n + 63)).');
319 cn = [cn; add];
320 if sum(add) <= eps*sum(cn)
321 break
322 end
323end
324cn = cn(:);
325end
326
327% -------------------------------------------------------------------------
328function v = i_phblk(nn, alv, Minv, tv, theta)
329v = zeros(numel(nn), 1);
330for i = 1:numel(nn)
331 v(i) = theta^nn(i)*(alv*(Minv^(nn(i)+1))*tv);
332end
333end
334
335% -------------------------------------------------------------------------
336function v = i_cquad(pdf, theta, nn, tmax)
337v = zeros(numel(nn), 1);
338for i = 1:numel(nn)
339 n = nn(i);
340 v(i) = i_dquad(@(x) exp(-theta*x + n*log(theta*x) - gammaln(n+1)), pdf, tmax);
341end
342end
343
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.
353%
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.
359if isfinite(tmax)
360 ulim = log(tmax);
361else
362 ulim = Inf;
363end
364v = integral(@(u) i_finite(w(exp(u)).*pdf(exp(u)).*exp(u)), -Inf, ulim, ...
365 'AbsTol', 1e-300, 'RelTol', 1e-13);
366end
367
368% -------------------------------------------------------------------------
369function y = i_finite(y)
370y(~isfinite(y)) = 0;
371end
372
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)));
377end