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 % Complementary arrival rate seen by station i at level n (flow balance
98 % across
the n<->n+1 cut of
the global LD birth-death marginal):
99 % lambda_i(n) = mu_i(n+1) * P_i(n+1) / P_i(n), n = 0..N-1.
100 % The global station i
is LD-exponential with ABSOLUTE rate
101 % mu(i,n)/L(i); flow balance gives
the absolute arrival rate lambda_i(n).
102 lam = zeros(1,N); % lam(n+1) holds lambda_i(n)
106 lam(n+1) = (mu(i,n+1)/L(i)) * Pi(n+2) / pn;
111 % Isolation returns absolute conditional throughput; convert to
the
112 % multiplier pfqn_mvald expects (multiplier = abs_rate * L(i)).
113 muabs = isol_condtput(lam, phRate{i}, phCompl{i}, N, nservers(i));
114 mu_new(i,:) = muabs * L(i);
116 delta = max(max(abs(mu_new - mu)));
118 X = XN(:); Q = QN(:); U = UN(:); C = CN(:);
125function muvec = isol_condtput(lam, phRate, phCompl, N, m)
126% Stationary analysis of a lambda(n)/Cox/1(-m) queue in isolation, returning
127%
the conditional throughput mu(n) = departure rate given n present, n=1..N.
128% lam(n+1) = arrival rate when n customers present (n=0..N-1);
the customer in
129% service advances through Coxian phases (rate phRate(k); completes w.p.
130% phCompl(k),
else advances to phase k+1). With m servers,
the phase-completion
131% rate at population n
is scaled by min(n,m).
133% State layout: 1 = empty;
for n=1..N, k=1..P -> index 1 + (n-1)*
P + k.
135idx = @(n,k) 1 + (n-1)*
P + k;
138% From empty: arrival starts a customer in phase 1.
139Gq(1, idx(1,1)) = Gq(1, idx(1,1)) + lam(1);
142 sc = min(n,m); % multiserver rate scaling
145 % Arrival (queueing; in-service phase preserved).
147 Gq(r, idx(n+1,k)) = Gq(r, idx(n+1,k)) + lam(n+1);
149 compl = phRate(k) * phCompl(k) * sc; % completion (departure)
150 adv = phRate(k) * (1-phCompl(k)) * sc; % advance to next phase
152 Gq(r, idx(n,k+1)) = Gq(r, idx(n,k+1)) + adv;
156 Gq(r, idx(n-1,1)) = Gq(r, idx(n-1,1)) + compl;
158 Gq(r, 1) = Gq(r, 1) + compl;
163Gq = Gq - diag(sum(Gq,2));
165% Stationary distribution: solve p*Gq = 0, sum(p) = 1.
176 dep = dep + pk * phRate(k) * phCompl(k) * min(n,m);
181 muvec(n) = min(n,m) / sum(1./phRate); % fallback: exponential-equiv rate
186% ========================= multiclass (R>1) path =========================
187function [X,Q,U,C,it,mu] = marie_multi(L,N,Z,scv,varargin)
188% Marie's method for multiclass FCFS Coxian closed networks. The aggregate
is
189% QD-AMVA with class-dependent scaling
beta_{i,r}(nvec) = (Coxian isolation
190% conditional throughput)/(exponential isolation conditional throughput), so
191% beta==1 recovers standard FCFS AMVA and
the cd-scaling carries only
the
192% non-exponential correction. beta
is supplied by a multiclass Cox/1 isolation
193% sub-model fed
the aggregate per-
class throughput (Baynat-Dallery isolation),
194% iterated to a fixed point on X.
197if nargin < 3 || isempty(Z), Z = zeros(1,R); end
199if nargin < 4 || isempty(scv), scv = ones(M,R); end
201tol = 1e-8; maxiter = 1000;
202if numel(varargin) >= 1 && ~isempty(varargin{1}), tol = varargin{1}; end
203if numel(varargin) >= 2 && ~isempty(varargin{2}), maxiter = varargin{2}; end
205% Exact product-form dispatch: exponential service that
is also
class-
206% independent at every station
is genuine BCMP FCFS -> exact MVA.
207isPF = all(scv(:) == 1);
210 if max(L(i,:)) - min(L(i,:)) > 1e-12
216 [XN,QN,UN,CN] = pfqn_mva(L,N,Z);
217 X = XN(:)
'; Q = QN; U = UN; C = CN; it = 0; mu = {}; return
220% Per-station per-class Coxian phase representation, plus an exponential
221% reference (same means) used to normalize the cd-scaling.
222phR = cell(M,R); phP = cell(M,R);
223eR = cell(M,R); eP = cell(M,R);
226 [~, mir, pir] = Coxian.fitMeanAndSCV(L(i,r), scv(i,r));
227 phR{i,r} = mir(:); phP{i,r} = pir(:);
228 eR{i,r} = 1/L(i,r); eP{i,r} = 1; % exponential reference
233for i = 1:M, cds{i} = @(nv) ones(1,R); end % beta = 1 initially
235Xprev = inf(1,R); it = 0;
236X = zeros(1,R); Q = zeros(M,R); U = zeros(M,R); C = zeros(M,R);
239 [X,Q,U,C] = amva_qd(L,N,Z,cds);
241 muCox = isol_mc(X, phR(i,:), phP(i,:), N);
242 muExp = isol_mc(X, eR(i,:), eP(i,:), N);
243 cds{i} = make_cdscale(muCox, muExp, N);
245 if max(abs(X - Xprev)) < tol, break, end
251function [X,Q,U,C] = amva_qd(L,N,Z,cds)
252% Multiclass Schweitzer AMVA with class-dependent service-rate scaling. The
253% effective class-r demand at station i is L(i,r)/beta_{i,r}(nvec_arrival),
254% beta supplied by cds{i} evaluated at the arrival-instant per-class population.
256Q = repmat(N,M,1) / max(M,1);
257W = zeros(M,R); X = zeros(1,R); U = zeros(M,R);
258Qprev = Q + 1; tol = 1e-9; it = 0;
259while max(abs(Q(:)-Qprev(:))) > tol && it < 5000
260 it = it + 1; Qprev = Q;
265 nv(r) = Q(i,r) * (N(r)-1) / N(r); % arrival instant, tagged class
268 % Work-based multiclass FCFS AMVA residence: the tagged class-r job's
269 % own effective service plus
the effective work of
the jobs found
270 % ahead (per-class, so unequal means are handled), with
the cd
271 % scaling
beta_{i,s} applied to each class
's effective demand.
273 W(i,r) = Leff(r) + sum(Leff .* nv);
275 denom = Z(r) + sum(W(:,r));
276 if denom > 0, X(r) = N(r) / denom; else X(r) = 0; end
278 Q(i,r) = X(r) * W(i,r);
285 U(i,r) = X(r) * L(i,r); % busy fraction (true mean service)
290function mumat = isol_mc(lam, phRrow, phProw, Nvec)
291% Stationary analysis of a multiclass lambda_r/Cox/1 FCFS queue in isolation
292% over the joint per-class population box [0..Nvec], with the head-of-line job
293% tracked as (class, phase) and, on a departure, the next head class drawn in
294% random order (prob n_c/sum(n)). Returns mumat{r}, an ND array over the box
295% giving the conditional class-r throughput mu_r(nvec) = (class-r departure
296% rate in states with population nvec)/P(nvec).
299for r = 1:R, Pc(r) = numel(phRrow{r}); end
303% Enumerate states: id map keyed by (popLinear, head, phase).
304% state 1 reserved for the empty station.
305key2id = containers.Map('KeyType
','char
','ValueType
','double
');
306ids = {}; % ids{s} = [popLinear, head, phase]
307key2id('E
') = 1; ids{1} = [1, 0, 0];
311 [popsubs{:}] = ind2sub(boxsz, p);
312 nvec = cell2mat(popsubs) - 1; % actual populations
313 if sum(nvec) == 0, continue, end
318 key2id(sprintf('%d_%d_%d
', p, c, k)) = nid;
319 ids{nid} = [p, c, k];
326 function id = getid(p, c, k)
330 id = key2id(sprintf('%d_%d_%d
', p, c, k));
333 function p = poplin(nvec)
334 sub = num2cell(nvec + 1);
335 p = sub2ind(boxsz, sub{:});
338I = zeros(0,1); J = zeros(0,1); V = zeros(0,1);
339 function addrate(a, b, rate)
340 I(end+1,1) = a; J(end+1,1) = b; V(end+1,1) = rate; %#ok<AGROW>
345 p = info(1); c = info(2); k = info(3);
349 [popsubs{:}] = ind2sub(boxsz, p);
350 nvec = cell2mat(popsubs) - 1;
354 if nvec(r) < Nvec(r) && lam(r) > 0
355 nnew = nvec; nnew(r) = nnew(r) + 1;
357 addrate(s, getid(poplin(nnew), r, 1), lam(r)); % start service
359 addrate(s, getid(poplin(nnew), c, k), lam(r)); % queue behind head
363 if c == 0, continue, end
365 compl = rate * phProw{c}(k);
366 adv = rate * (1 - phProw{c}(k));
367 if adv > 0 && k < Pc(c)
368 addrate(s, getid(p, c, k+1), adv);
371 nnew = nvec; nnew(c) = nnew(c) - 1;
373 addrate(s, 1, compl);
378 addrate(s, getid(poplin(nnew), cp, 1), compl * nnew(cp)/tot);
385Gq = sparse(I, J, V, S, S);
386Gq = Gq - spdiags(sum(Gq,2), 0, S, S);
388% Stationary distribution.
393% Conditional
class-r throughput on
the population lattice.
395for r = 1:R, mumat{r} = zeros(boxsz); end
398for r = 1:R, dep{r} = zeros(boxsz); end
401 p = info(1); c = info(2); k = info(3);
402 if c == 0,
continue, end
403 Ppop(p) = Ppop(p) + pvec(s);
404 dep{c}(p) = dep{c}(p) + pvec(s) * phRrow{c}(k) * phProw{c}(k);
408 mumat{r}(idxpos) = dep{r}(idxpos) ./ Ppop(idxpos);
412function f = make_cdscale(muCox, muExp, Nvec)
413% Class-dependent scaling
beta_{i,r}(nv) = muCox_r(nv)/muExp_r(nv), so beta==1
414%
for exponential service. Multilinear interpolation over
the population
415% lattice; guarded and clamped.
416f = @(nv) cdscale_eval(nv, muCox, muExp, Nvec);
419function be = cdscale_eval(nv, muCox, muExp, Nvec)
423 num = ndlininterp(muCox{r}, nv, Nvec);
424 den = ndlininterp(muExp{r}, nv, Nvec);
425 if den > 0 && num > 0 && isfinite(num) && isfinite(den)
431be = min(max(be, 1e-3), 1e3);
434function v = ndlininterp(A, x, Nvec)
435% Multilinear interpolation of ND array A (size Nvec+1) at real point x,
436% clamped to
the box [0, Nvec].
439x = min(max(x(:)', 0), Nvec);
441hi = min(lo + 1, Nvec);
444for mask = 0:(2^R - 1)
445 w = 1; sub = zeros(1,R);
448 sub(d) = hi(d); w = w * fr(d);
450 sub(d) = lo(d); w = w * (1 - fr(d));
453 if w == 0, continue, end
454 subc = num2cell(sub + 1);
455 v = v + w * A(sub2ind(sz, subc{:}));