LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
pfqn_marie.m
1%{
2%{
3 % @file pfqn_marie.m
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.
7%}
8%}
9
10function [X,Q,U,C,it,mu] = pfqn_marie(L,N,Z,scv,varargin)
11%{
12%{
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).
42%}
43%}
44
45R = size(L,2);
46if R > 1
47 [X,Q,U,C,it,mu] = marie_multi(L,N,Z,scv,varargin{:});
48 return
49end
50
51L = L(:);
52M = numel(L);
53if nargin < 3 || isempty(Z)
54 Z = 0;
55end
56Z = sum(Z(:));
57if nargin < 4 || isempty(scv)
58 scv = ones(M,1);
59end
60scv = scv(:);
61
62tol = 1e-8;
63maxiter = 1000;
64nservers = ones(M,1);
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
69
70% Per-station Coxian phase representation of the service (mean = L(i), scv(i)).
71phRate = cell(M,1);
72phCompl = cell(M,1);
73for i = 1:M
74 [~, mu_i, phi_i] = Coxian.fitMeanAndSCV(L(i), scv(i));
75 phRate{i} = mu_i(:);
76 phCompl{i} = phi_i(:);
77end
78
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).
82mu = zeros(M,N);
83for i = 1:M
84 mu(i,:) = min(1:N, nservers(i));
85end
86
87X = zeros(M,1); Q = zeros(M,1); U = zeros(M,1); C = zeros(M,1);
88it = 0;
89while it < maxiter
90 it = it + 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)
94 mu_new = mu;
95 for i = 1:M
96 Pi = Pg(i,1:N+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)
103 for n = 0:N-1
104 pn = Pi(n+1);
105 if pn > 0
106 lam(n+1) = (mu(i,n+1)/L(i)) * Pi(n+2) / pn;
107 else
108 lam(n+1) = 0;
109 end
110 end
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);
115 end
116 delta = max(max(abs(mu_new - mu)));
117 mu = mu_new;
118 X = XN(:); Q = QN(:); U = UN(:); C = CN(:);
119 if delta < tol
120 break
121 end
122end
123end
124
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).
132P = numel(phRate);
133% State layout: 1 = empty; for n=1..N, k=1..P -> index 1 + (n-1)*P + k.
134S = 1 + N*P;
135idx = @(n,k) 1 + (n-1)*P + k;
136Gq = zeros(S,S);
137
138% From empty: arrival starts a customer in phase 1.
139Gq(1, idx(1,1)) = Gq(1, idx(1,1)) + lam(1);
140
141for n = 1:N
142 sc = min(n,m); % multiserver rate scaling
143 for k = 1:P
144 r = idx(n,k);
145 % Arrival (queueing; in-service phase preserved).
146 if n < N
147 Gq(r, idx(n+1,k)) = Gq(r, idx(n+1,k)) + lam(n+1);
148 end
149 compl = phRate(k) * phCompl(k) * sc; % completion (departure)
150 adv = phRate(k) * (1-phCompl(k)) * sc; % advance to next phase
151 if adv > 0 && k < P
152 Gq(r, idx(n,k+1)) = Gq(r, idx(n,k+1)) + adv;
153 end
154 if compl > 0
155 if n > 1
156 Gq(r, idx(n-1,1)) = Gq(r, idx(n-1,1)) + compl;
157 else
158 Gq(r, 1) = Gq(r, 1) + compl;
159 end
160 end
161 end
162end
163Gq = Gq - diag(sum(Gq,2));
164
165% Stationary distribution: solve p*Gq = 0, sum(p) = 1.
166A = [Gq'; ones(1,S)];
167b = [zeros(S,1); 1];
168p = (A \ b)';
169
170muvec = zeros(1,N);
171for n = 1:N
172 Pn = 0; dep = 0;
173 for k = 1:P
174 pk = p(idx(n,k));
175 Pn = Pn + pk;
176 dep = dep + pk * phRate(k) * phCompl(k) * min(n,m);
177 end
178 if Pn > 0
179 muvec(n) = dep / Pn;
180 else
181 muvec(n) = min(n,m) / sum(1./phRate); % fallback: exponential-equiv rate
182 end
183end
184end
185
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.
195[M,R] = size(L);
196N = N(:)';
197if nargin < 3 || isempty(Z), Z = zeros(1,R); end
198Z = Z(:)';
199if nargin < 4 || isempty(scv), scv = ones(M,R); end
200
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
204
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);
208if isPF
209 for i = 1:M
210 if max(L(i,:)) - min(L(i,:)) > 1e-12
211 isPF = false; break
212 end
213 end
214end
215if isPF
216 [XN,QN,UN,CN] = pfqn_mva(L,N,Z);
217 X = XN(:)'; Q = QN; U = UN; C = CN; it = 0; mu = {}; return
218end
219
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);
224for i = 1:M
225 for r = 1: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
229 end
230end
231
232cds = cell(M,1);
233for i = 1:M, cds{i} = @(nv) ones(1,R); end % beta = 1 initially
234
235Xprev = inf(1,R); it = 0;
236X = zeros(1,R); Q = zeros(M,R); U = zeros(M,R); C = zeros(M,R);
237while it < maxiter
238 it = it + 1;
239 [X,Q,U,C] = amva_qd(L,N,Z,cds);
240 for i = 1:M
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);
244 end
245 if max(abs(X - Xprev)) < tol, break, end
246 Xprev = X;
247end
248mu = cds;
249end
250
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.
255[M,R] = size(L);
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;
261 for r = 1:R
262 for i = 1:M
263 nv = Q(i,:);
264 if N(r) > 0
265 nv(r) = Q(i,r) * (N(r)-1) / N(r); % arrival instant, tagged class
266 end
267 be = cds{i}(nv);
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.
272 Leff = L(i,:) ./ be;
273 W(i,r) = Leff(r) + sum(Leff .* nv);
274 end
275 denom = Z(r) + sum(W(:,r));
276 if denom > 0, X(r) = N(r) / denom; else X(r) = 0; end
277 for i = 1:M
278 Q(i,r) = X(r) * W(i,r);
279 end
280 end
281end
282C = W;
283for r = 1:R
284 for i = 1:M
285 U(i,r) = X(r) * L(i,r); % busy fraction (true mean service)
286 end
287end
288end
289
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).
297R = numel(lam);
298Pc = zeros(1,R);
299for r = 1:R, Pc(r) = numel(phRrow{r}); end
300boxsz = Nvec + 1;
301npops = prod(boxsz);
302
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];
308nid = 1;
309popsubs = cell(1,R);
310for p = 1:npops
311 [popsubs{:}] = ind2sub(boxsz, p);
312 nvec = cell2mat(popsubs) - 1; % actual populations
313 if sum(nvec) == 0, continue, end
314 for c = 1:R
315 if nvec(c) > 0
316 for k = 1:Pc(c)
317 nid = nid + 1;
318 key2id(sprintf('%d_%d_%d', p, c, k)) = nid;
319 ids{nid} = [p, c, k];
320 end
321 end
322 end
323end
324S = nid;
325
326 function id = getid(p, c, k)
327 if c == 0
328 id = 1;
329 else
330 id = key2id(sprintf('%d_%d_%d', p, c, k));
331 end
332 end
333 function p = poplin(nvec)
334 sub = num2cell(nvec + 1);
335 p = sub2ind(boxsz, sub{:});
336 end
337
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>
341 end
342
343for s = 1:S
344 info = ids{s};
345 p = info(1); c = info(2); k = info(3);
346 if c == 0
347 nvec = zeros(1,R);
348 else
349 [popsubs{:}] = ind2sub(boxsz, p);
350 nvec = cell2mat(popsubs) - 1;
351 end
352 % arrivals
353 for r = 1:R
354 if nvec(r) < Nvec(r) && lam(r) > 0
355 nnew = nvec; nnew(r) = nnew(r) + 1;
356 if c == 0
357 addrate(s, getid(poplin(nnew), r, 1), lam(r)); % start service
358 else
359 addrate(s, getid(poplin(nnew), c, k), lam(r)); % queue behind head
360 end
361 end
362 end
363 if c == 0, continue, end
364 rate = phRrow{c}(k);
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);
369 end
370 if compl > 0
371 nnew = nvec; nnew(c) = nnew(c) - 1;
372 if sum(nnew) == 0
373 addrate(s, 1, compl);
374 else
375 tot = sum(nnew);
376 for cp = 1:R
377 if nnew(cp) > 0
378 addrate(s, getid(poplin(nnew), cp, 1), compl * nnew(cp)/tot);
379 end
380 end
381 end
382 end
383end
384
385Gq = sparse(I, J, V, S, S);
386Gq = Gq - spdiags(sum(Gq,2), 0, S, S);
387
388% Stationary distribution.
389A = [Gq'; ones(1,S)];
390b = [zeros(S,1); 1];
391pvec = A \ b;
392
393% Conditional class-r throughput on the population lattice.
394mumat = cell(1,R);
395for r = 1:R, mumat{r} = zeros(boxsz); end
396Ppop = zeros(boxsz);
397dep = cell(1,R);
398for r = 1:R, dep{r} = zeros(boxsz); end
399for s = 1:S
400 info = ids{s};
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);
405end
406for r = 1:R
407 idxpos = Ppop > 0;
408 mumat{r}(idxpos) = dep{r}(idxpos) ./ Ppop(idxpos);
409end
410end
411
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);
417end
418
419function be = cdscale_eval(nv, muCox, muExp, Nvec)
420R = numel(Nvec);
421be = ones(1,R);
422for r = 1:R
423 num = ndlininterp(muCox{r}, nv, Nvec);
424 den = ndlininterp(muExp{r}, nv, Nvec);
425 if den > 0 && num > 0 && isfinite(num) && isfinite(den)
426 be(r) = num / den;
427 else
428 be(r) = 1;
429 end
430end
431be = min(max(be, 1e-3), 1e3);
432end
433
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].
437R = numel(Nvec);
438sz = Nvec + 1;
439x = min(max(x(:)', 0), Nvec);
440lo = floor(x);
441hi = min(lo + 1, Nvec);
442fr = x - lo;
443v = 0;
444for mask = 0:(2^R - 1)
445 w = 1; sub = zeros(1,R);
446 for d = 1:R
447 if bitget(mask, d)
448 sub(d) = hi(d); w = w * fr(d);
449 else
450 sub(d) = lo(d); w = w * (1 - fr(d));
451 end
452 end
453 if w == 0, continue, end
454 subc = num2cell(sub + 1);
455 v = v + w * A(sub2ind(sz, subc{:}));
456end
457end
Definition Station.m:245