4 % @brief Marie
's iterative aggregation for closed networks with FCFS Coxian
5 % (non-exponential) service. Single-class exact-reducing; multiclass via
6 % QD-AMVA with class-dependent (cd) scaling.
10function [X,Q,U,C,it,mu] = pfqn_marie(L,N,Z,scv,varargin)
13 % @brief Marie's method (Marie 1979/1980): approximate mean performance of a
14 % closed queueing network with FCFS general (Coxian) service, via
15 % iterative aggregation-decomposition. Each station
is analyzed in
16 % isolation as a lambda(n)/Cox/1 queue; the resulting conditional
17 % throughputs mu_i(n) drive a load-dependent aggregate solve, iterated
18 % to a fixed point. Single
class (R=1): the aggregate
is the exact LD
19 % product-
form solve pfqn_mvald, and the method reduces to exact product
20 %
form for exponential service. Multiple classes (R>1): the aggregate
is
21 % QD-AMVA with class-dependent (cd) scaling
beta_{i,r}(nvec) supplied by
22 % a multiclass Cox/1 isolation sub-model; exact product-
form service
23 % (scv==1 with
class-independent means)
is dispatched to exact MVA,
24 % otherwise the result
is a decomposition approximation.
25 % @fn pfqn_marie(L, N, Z, scv, tol, maxiter, nservers)
26 % @param L Service demand matrix (M x R).
27 % @param N Population vector (1 x R).
28 % @param Z Think time vector (1 x R; total delay demand per
class).
29 % @param scv Per-station per-
class squared coefficient of variation (M x R).
30 % scv==1 exponential; scv<0.5 Erlang; scv>0.5 two-phase Coxian.
31 % @param tol Convergence tolerance (
default 1e-8).
32 % @param maxiter Maximum iterations (
default 1000).
33 % @param nservers Per-station server count (M x 1,
default all 1); single-
class
34 % only (multiserver multiclass isolation
is not yet supported).
35 % @
return X Throughput: single
class M x 1 (per station); multiclass 1 x R
36 % (per-
class chain throughput,
visits folded into L).
37 % @
return Q Mean queue length (M x 1 single
class, M x R multiclass).
38 % @
return U Utilization (same shape as Q).
39 % @
return C Residence time (same shape as Q).
40 % @
return it Iterations performed.
41 % @
return mu Converged LD data (M x N single
class; cell of cd-scalings R>1).
47 [X,Q,U,C,it,mu] = marie_multi(L,N,Z,scv,varargin{:});
53if nargin < 3 || isempty(Z)
57if nargin < 4 || isempty(scv)
65if numel(varargin) >= 1 && ~isempty(varargin{1}), tol = varargin{1}; end
66if numel(varargin) >= 2 && ~isempty(varargin{2}), maxiter = varargin{2}; end
67if numel(varargin) >= 3 && ~isempty(varargin{3}), nservers = varargin{3}(:); end
68if isscalar(nservers), nservers = nservers*ones(M,1); end
70% Per-station Coxian phase representation of the service (mean = L(i), scv(i)).
74 [~, mu_i, phi_i] = Coxian.fitMeanAndSCV(L(i), scv(i));
76 phCompl{i} = phi_i(:);
79% Initial LD rate multipliers (relative to base rate 1/L(i)): exponential
80% single-server guess mu=1, multiserver mu=min(n,m). pfqn_mvald interprets mu
81% as a multiplier, so the absolute service rate at n jobs
is mu(i,n)/L(i).
84 mu(i,:) = min(1:N, nservers(i));
87X = zeros(M,1); Q = zeros(M,1); U = zeros(M,1); C = zeros(M,1);
91 [XN,QN,UN,CN,~,~,piglob] = pfqn_mvald(L,N,Z,mu);
92 % Marginal queue-length distribution at full population, per station.
93 Pg = piglob(:,:,end); % M x (sum(N)+1), Pg(i,k) =
P(n_i = k-1)
97 % see _kb/03-api-layer.md (pfqn/ family: scaling, log-domain switches, dispatch gates)
98 lam = zeros(1,N); % lam(n+1) holds lambda_i(n)
102 lam(n+1) = (mu(i,n+1)/L(i)) * Pi(n+2) / pn;
107 % Isolation returns absolute conditional throughput; convert to the
108 % multiplier pfqn_mvald expects (multiplier = abs_rate * L(i)).
109 muabs = isol_condtput(lam, phRate{i}, phCompl{i}, N, nservers(i));
110 mu_new(i,:) = muabs * L(i);
112 delta = max(max(abs(mu_new - mu)));
114 X = XN(:); Q = QN(:); U = UN(:); C = CN(:);
121function muvec = isol_condtput(lam, phRate, phCompl, N, m)
122% Stationary analysis of a lambda(n)/Cox/1(-m) queue in isolation, returning
123% the conditional throughput mu(n) = departure rate given n present, n=1..N.
124% lam(n+1) = arrival rate when n customers present (n=0..N-1); the customer in
125% service advances through Coxian phases (rate phRate(k); completes w.p.
126% phCompl(k),
else advances to phase k+1). With m servers, the phase-completion
127% rate at population n
is scaled by min(n,m).
129% State layout: 1 = empty;
for n=1..N, k=1..P -> index 1 + (n-1)*
P + k.
131idx = @(n,k) 1 + (n-1)*
P + k;
134% From empty: arrival starts a customer in phase 1.
135Gq(1, idx(1,1)) = Gq(1, idx(1,1)) + lam(1);
138 sc = min(n,m); % multiserver rate scaling
141 % Arrival (queueing; in-service phase preserved).
143 Gq(r, idx(n+1,k)) = Gq(r, idx(n+1,k)) + lam(n+1);
145 compl = phRate(k) * phCompl(k) * sc; % completion (departure)
146 adv = phRate(k) * (1-phCompl(k)) * sc; % advance to next phase
148 Gq(r, idx(n,k+1)) = Gq(r, idx(n,k+1)) + adv;
152 Gq(r, idx(n-1,1)) = Gq(r, idx(n-1,1)) + compl;
154 Gq(r, 1) = Gq(r, 1) + compl;
159Gq = Gq - diag(sum(Gq,2));
161% Stationary distribution: solve p*Gq = 0, sum(p) = 1.
172 dep = dep + pk * phRate(k) * phCompl(k) * min(n,m);
177 muvec(n) = min(n,m) / sum(1./phRate); % fallback: exponential-equiv rate
182% ========================= multiclass (R>1) path =========================
183function [X,Q,U,C,it,mu] = marie_multi(L,N,Z,scv,varargin)
184% Marie's method for multiclass FCFS Coxian closed networks. The aggregate
is
185% QD-AMVA with class-dependent scaling
beta_{i,r}(nvec) = (Coxian isolation
186% conditional throughput)/(exponential isolation conditional throughput), so
187% beta==1 recovers standard FCFS AMVA and the cd-scaling carries only the
188% non-exponential correction. beta
is supplied by a multiclass Cox/1 isolation
189% sub-model fed the aggregate per-
class throughput (Baynat-Dallery isolation),
190% iterated to a fixed point on X.
193if nargin < 3 || isempty(Z), Z = zeros(1,R); end
195if nargin < 4 || isempty(scv), scv = ones(M,R); end
197tol = 1e-8; maxiter = 1000;
198if numel(varargin) >= 1 && ~isempty(varargin{1}), tol = varargin{1}; end
199if numel(varargin) >= 2 && ~isempty(varargin{2}), maxiter = varargin{2}; end
201% Exact product-
form dispatch: exponential service that
is also
class-
202% independent at every station
is genuine BCMP FCFS -> exact MVA.
203isPF = all(scv(:) == 1);
206 if max(L(i,:)) - min(L(i,:)) > 1e-12
212 [XN,QN,UN,CN] = pfqn_mva(L,N,Z);
213 X = XN(:)
'; Q = QN; U = UN; C = CN; it = 0; mu = {}; return
216% Per-station per-class Coxian phase representation, plus an exponential
217% reference (same means) used to normalize the cd-scaling.
218phR = cell(M,R); phP = cell(M,R);
219eR = cell(M,R); eP = cell(M,R);
222 [~, mir, pir] = Coxian.fitMeanAndSCV(L(i,r), scv(i,r));
223 phR{i,r} = mir(:); phP{i,r} = pir(:);
224 eR{i,r} = 1/L(i,r); eP{i,r} = 1; % exponential reference
229for i = 1:M, cds{i} = @(nv) ones(1,R); end % beta = 1 initially
231Xprev = inf(1,R); it = 0;
232X = zeros(1,R); Q = zeros(M,R); U = zeros(M,R); C = zeros(M,R);
235 [X,Q,U,C] = amva_qd(L,N,Z,cds);
237 muCox = isol_mc(X, phR(i,:), phP(i,:), N);
238 muExp = isol_mc(X, eR(i,:), eP(i,:), N);
239 cds{i} = make_cdscale(muCox, muExp, N);
241 if max(abs(X - Xprev)) < tol, break, end
247function [X,Q,U,C] = amva_qd(L,N,Z,cds)
248% Multiclass Schweitzer AMVA with class-dependent service-rate scaling. The
249% effective class-r demand at station i is L(i,r)/beta_{i,r}(nvec_arrival),
250% beta supplied by cds{i} evaluated at the arrival-instant per-class population.
252Q = repmat(N,M,1) / max(M,1);
253W = zeros(M,R); X = zeros(1,R); U = zeros(M,R);
254Qprev = Q + 1; tol = 1e-9; it = 0;
255while max(abs(Q(:)-Qprev(:))) > tol && it < 5000
256 it = it + 1; Qprev = Q;
261 nv(r) = Q(i,r) * (N(r)-1) / N(r); % arrival instant, tagged class
264 % Work-based multiclass FCFS AMVA residence: the tagged class-r job's
265 % own effective service plus the effective work of the jobs found
266 % ahead (per-class, so unequal means are handled), with the cd
267 % scaling
beta_{i,s} applied to each class
's effective demand.
269 W(i,r) = Leff(r) + sum(Leff .* nv);
271 denom = Z(r) + sum(W(:,r));
272 if denom > 0, X(r) = N(r) / denom; else X(r) = 0; end
274 Q(i,r) = X(r) * W(i,r);
281 U(i,r) = X(r) * L(i,r); % busy fraction (true mean service)
286function mumat = isol_mc(lam, phRrow, phProw, Nvec)
287% Stationary analysis of a multiclass lambda_r/Cox/1 FCFS queue in isolation
288% over the joint per-class population box [0..Nvec], with the head-of-line job
289% tracked as (class, phase) and, on a departure, the next head class drawn in
290% random order (prob n_c/sum(n)). Returns mumat{r}, an ND array over the box
291% giving the conditional class-r throughput mu_r(nvec) = (class-r departure
292% rate in states with population nvec)/P(nvec).
295for r = 1:R, Pc(r) = numel(phRrow{r}); end
299% Enumerate states: id map keyed by (popLinear, head, phase).
300% state 1 reserved for the empty station.
301key2id = containers.Map('KeyType
','char
','ValueType
','double
');
302ids = {}; % ids{s} = [popLinear, head, phase]
303key2id('E
') = 1; ids{1} = [1, 0, 0];
307 [popsubs{:}] = ind2sub(boxsz, p);
308 nvec = cell2mat(popsubs) - 1; % actual populations
309 if sum(nvec) == 0, continue, end
314 key2id(sprintf('%d_%d_%d
', p, c, k)) = nid;
315 ids{nid} = [p, c, k];
322 function id = getid(p, c, k)
326 id = key2id(sprintf('%d_%d_%d
', p, c, k));
329 function p = poplin(nvec)
330 sub = num2cell(nvec + 1);
331 p = sub2ind(boxsz, sub{:});
334I = zeros(0,1); J = zeros(0,1); V = zeros(0,1);
335 function addrate(a, b, rate)
336 I(end+1,1) = a; J(end+1,1) = b; V(end+1,1) = rate; %#ok<AGROW>
341 p = info(1); c = info(2); k = info(3);
345 [popsubs{:}] = ind2sub(boxsz, p);
346 nvec = cell2mat(popsubs) - 1;
350 if nvec(r) < Nvec(r) && lam(r) > 0
351 nnew = nvec; nnew(r) = nnew(r) + 1;
353 addrate(s, getid(poplin(nnew), r, 1), lam(r)); % start service
355 addrate(s, getid(poplin(nnew), c, k), lam(r)); % queue behind head
359 if c == 0, continue, end
361 compl = rate * phProw{c}(k);
362 adv = rate * (1 - phProw{c}(k));
363 if adv > 0 && k < Pc(c)
364 addrate(s, getid(p, c, k+1), adv);
367 nnew = nvec; nnew(c) = nnew(c) - 1;
369 addrate(s, 1, compl);
374 addrate(s, getid(poplin(nnew), cp, 1), compl * nnew(cp)/tot);
381Gq = sparse(I, J, V, S, S);
382Gq = Gq - spdiags(sum(Gq,2), 0, S, S);
384% Stationary distribution.
389% Conditional
class-r throughput on the population lattice.
391for r = 1:R, mumat{r} = zeros(boxsz); end
394for r = 1:R, dep{r} = zeros(boxsz); end
397 p = info(1); c = info(2); k = info(3);
398 if c == 0,
continue, end
399 Ppop(p) = Ppop(p) + pvec(s);
400 dep{c}(p) = dep{c}(p) + pvec(s) * phRrow{c}(k) * phProw{c}(k);
404 mumat{r}(idxpos) = dep{r}(idxpos) ./ Ppop(idxpos);
408function f = make_cdscale(muCox, muExp, Nvec)
409% Class-dependent scaling
beta_{i,r}(nv) = muCox_r(nv)/muExp_r(nv), so beta==1
410%
for exponential service. Multilinear interpolation over the population
411% lattice; guarded and clamped.
412f = @(nv) cdscale_eval(nv, muCox, muExp, Nvec);
415function be = cdscale_eval(nv, muCox, muExp, Nvec)
419 num = ndlininterp(muCox{r}, nv, Nvec);
420 den = ndlininterp(muExp{r}, nv, Nvec);
421 if den > 0 && num > 0 && isfinite(num) && isfinite(den)
427be = min(max(be, 1e-3), 1e3);
430function v = ndlininterp(A, x, Nvec)
431% Multilinear interpolation of ND array A (size Nvec+1) at real point x,
432% clamped to the box [0, Nvec].
435x = min(max(x(:)', 0), Nvec);
437hi = min(lo + 1, Nvec);
440for mask = 0:(2^R - 1)
441 w = 1; sub = zeros(1,R);
444 sub(d) = hi(d); w = w * fr(d);
446 sub(d) = lo(d); w = w * (1 - fr(d));
449 if w == 0, continue, end
450 subc = num2cell(sub + 1);
451 v = v + w * A(sub2ind(sz, subc{:}));