LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
pfqn_mvac.m
1%{
2%{
3 % @file pfqn_mvac.m
4 % @brief MVAC (Mean Value Analysis by Chain) for product-form queueing networks.
5%}
6%}
7
8%{
9%{
10 % @brief MVAC (Mean Value Analysis by Chain) for product-form queueing networks.
11 % @fn pfqn_mvac(L, N, Z)
12 % @param L Service demand matrix of the single-server fixed-rate queues (M x R).
13 % @param N Population vector (1 x R).
14 % @param Z Think time matrix of the infinite-server centers (1 x R or Mz x R).
15 % @return XN Per-class throughput (1 x R).
16 % @return QN Per-class mean queue-length at the queues (M x R).
17 % @return UN Per-class utilization at the queues (M x R).
18 % @return CN Per-class residence time at the queues (M x R).
19%}
20%}
21function [XN,QN,UN,CN] = pfqn_mvac(L,N,Z)
22% [XN,QN,UN,CN] = PFQN_MVAC(L,N,Z)
23%
24% Exact mean value analysis by chain (MVAC) of a closed multichain product-form
25% queueing network composed of single-server fixed-rate (SSFR) queues and
26% infinite-server (IS) centers, as given in Conway, de Souza e Silva and
27% Lavenberg, "Mean Value Analysis by Chain of Product Form Queueing Networks",
28% IEEE Trans. Computers 38(3):432-442, 1989.
29%
30% Unlike the classic MVA recursion of PFQN_MVA, which recurs on the population
31% vector and therefore costs O(prod(N+1)), MVAC recurs on the *chains*: each
32% chain is reduced to single-customer chains and the removed chains are replaced
33% by self-looping single-customer (SCSL) chains pinned at a service center. The
34% multiplicity vector v = (v_1,...,v_J), where v_j is the number of SCSL chains
35% at center j, indexes the recursion in place of the population vector. MVAC is
36% therefore attractive for networks with few centers and many chains, where its
37% cost grows only polynomially in the number of distinct chains, and it is the
38% mean-value counterpart of the RECAL normalizing-constant recursion of
39% PFQN_RECAL. Since no normalizing constant is formed, MVAC does not suffer the
40% floating-point underflow/overflow that complicates RECAL and convolution.
41%
42% Throughout, j and i index service centers (j = 1,...,J1 are SSFR and
43% j = J1+1,...,J are IS), k and l index single-customer chains, a_jk = theta_jk
44% T_jk is the relative utilization of chain k at center j (i.e., its service
45% demand), a_k = sum_j a_jk, and I_k = {v : sum_j v_j = K - k} with K the total
46% number of single-customer chains. Writing L^k_j(v) for the mean number of
47% customers at center j (SCSL customers excluded), L^k_{jl}(v) for the mean
48% number of chain-l customers at center j, and lambda^k_k(v) for the throughput
49% of chain k, all for the network with normalizing constant G_k(v), the
50% recursion of Section II of the paper reads
51% lambda^k_k(v) = 1 / (a_k + sum_{j=1}^{J1} a_jk (L^{k-1}_j(v) + v_j)), (10)
52% L^k_{jk}(v) = lambda^k_k(v) a_jk (1 + L^{k-1}_j(v) + v_j), j SSFR, (9a)
53% L^k_{jk}(v) = lambda^k_k(v) a_jk, j IS, (9b)
54% L^k_i(v) = sum_j L^k_{jk}(v) L^{k-1}_i(v + 1_j) + L^k_{ik}(v), (7)
55% L^k_{il}(v) = sum_j L^k_{jk}(v) L^{k-1}_{il}(v + 1_j), l = 1,...,k-1 (6)
56% with L^0_j(v) = 0. Equation (10) is the arrival-theorem closure obtained by
57% summing (9a)-(9b) over all centers, since chain k holds a single customer.
58% The measures of the original network are read off at k = K and v = 0.
59%
60% Part 1 of the basic step evaluates (10), (9) and (7) and yields the measures
61% of chain K; part 2 evaluates (6) and yields those of the chains that visit at
62% least one IS center, whose throughput then follows from Little's law at that
63% center. Chains that visit only SSFR centers require instead a re-execution of
64% part 1 with their label interchanged with K, which is cheap because the levels
65% below the interchanged label are unaffected and are reused from the first
66% execution. Classes with N_r > 1, and classes with identical demand columns,
67% collapse into a single subset of identical single-customer chains: only one
68% representative per subset is analyzed and its per-chain measures are scaled by
69% the class population, so the cost depends on the number D of *distinct* chains
70% rather than on K.
71%
73% L - (M x R) service demand matrix of the SSFR queues.
74% N - (1 x R) closed population vector, finite and nonnegative.
75% Z - (1 x R) or (Mz x R) service demand matrix of the IS centers; each row is
76% one IS center. Defaults to zeros(1,R).
77%
78% Returns:
79% XN - (1 x R) per-class throughput at the reference station.
80% QN - (M x R) per-class mean queue-length at the SSFR queues.
81% UN - (M x R) per-class utilization, XN(r) * L(i,r).
82% CN - (M x R) per-class residence time, QN(i,r) / XN(r).
83%
84% See also PFQN_MVA, PFQN_RECAL, PFQN_CONV.
85%
86% Copyright (c) 2012-2026, Imperial College London
87% All rights reserved.
88
89[M,R] = size(L);
90if nargin < 3 || isempty(Z)
91 Z = zeros(1,R);
92end
93if size(Z,2) ~= R
94 line_error(mfilename,'the think time matrix and the demand matrix have a different number of classes');
95end
96N = round(N(:)');
97if length(N) ~= R
98 line_error(mfilename,'the demand matrix and the population vector have a different number of classes');
99end
100if any(N < 0) || any(isinf(N))
101 line_error(mfilename,'the population vector must be finite and nonnegative');
102end
103
104XN = zeros(1,R);
105QN = zeros(M,R);
106UN = zeros(M,R);
107CN = zeros(M,R);
108
109K = sum(N); % number of single-customer chains in the original network
110if K == 0
111 return
112end
113
114% Discard the centers that no chain visits: they carry no customers and would
115% only inflate the multiplicity vector v.
116ssfrIdx = find(any(L > 0, 2))';
117isIdx = find(any(Z > 0, 2))';
118J1 = length(ssfrIdx);
119J = J1 + length(isIdx);
120if J == 0
121 line_error(mfilename,'all service demands are zero, the throughput is unbounded');
122end
123% A(j,r) = a_jr, centers ordered SSFR first as required by (9a)-(9b) and (10)
124A = [L(ssfrIdx,:); Z(isIdx,:)];
125
126%% Partition the chains into subsets of identical single-customer chains.
127% Two chains are identical when they have the same demand at every center, so
128% the subsets are the distinct columns of A restricted to the populated classes.
129posr = find(N > 0);
130[Adist,~,grpOfClass] = unique(A(:,posr)','rows','stable');
131Dall = size(Adist,1);
132visitsIS = false(1,Dall);
133for g = 1:Dall
134 visitsIS(g) = any(Adist(g,(J1+1):J) > 0);
135end
136% Chains that visit at least one IS center are labelled K-D+1,...,K-S and are
137% resolved by part 2; the S chains that visit only SSFR centers are labelled
138% K-S+1,...,K and are resolved by re-executions of part 1.
139gorder = [find(visitsIS), find(~visitsIS)];
140D = Dall;
141S = sum(~visitsIS);
142Agrp = Adist(gorder,:)'; % (J x D) demands of the representative of each subset
143mult = zeros(1,D);
144for g = 1:D
145 mult(g) = sum(N(posr(grpOfClass == gorder(g))));
146end
147
148% Chain labels: the D representatives are labelled K-D+1,...,K, the remaining
149% K-D identical chains fill the labels 1,...,K-D in any order.
150chainGroup = zeros(1,K);
151chainGroup((K-D+1):K) = 1:D;
152p = 0;
153for g = 1:D
154 for c = 1:(mult(g)-1)
155 p = p + 1;
156 chainGroup(p) = g;
157 end
158end
159a = Agrp(:,chainGroup); % (J x K) relative utilizations a_jk
160ak = sum(a,1); % (1 x K) a_k = sum_j a_jk, over ALL centers
161
162%% Enumerate the multiplicity vectors.
163% The basic step at level k sweeps V_k = I_k u ... u I_K = {v : sum_j v_j <= K-k}
164% and reads level k-1 at v + 1_j, whose component sum is at most K-k+1. Vectors
165% with sum K are therefore enumerated as well, so that v + 1_j is always in
166% range; they are only ever read at level 0, where L^0 = 0.
167Vlist = [];
168for t = 0:K
169 Vlist = [Vlist; multichoose(J,t)]; %#ok<AGROW>
170end
171nv = size(Vlist,1);
172vsum = sum(Vlist,2);
173% succ(vi,j) is the index of v + 1_j, or 0 when out of range (sum(v) == K)
174succ = zeros(nv,J);
175for j = 1:J
176 Vplus = Vlist;
177 Vplus(:,j) = Vplus(:,j) + 1;
178 [tf,loc] = ismember(Vplus,Vlist,'rows');
179 succ(:,j) = loc .* tf;
180end
181z0 = find(vsum == 0,1); % index of v = 0
182
183%% MVAC recursion.
184Lall = cell(1,K+1); % Lall{k+1}(i,vi) = L^k_i(v)
185Ljkall = cell(1,K+1); % Ljkall{k+1}(j,vi) = L^k_{jk}(v)
186lamall = cell(1,K+1); % lamall{k+1}(vi) = lambda^k_k(v)
187Lall{1} = zeros(J,nv); % L^0_i(v) = 0
188
189lamChain = zeros(1,K); % per-chain throughput, indexed by the ORIGINAL chain label
190Lchain = zeros(J,K); % per-chain mean queue-length, same indexing
191
192% First execution of the basic step, k0 = 1.
193[Lall,Ljkall,lamall] = mvac_part1(1,K,J,J1,a,ak,Vlist,vsum,succ,Lall,Ljkall,lamall);
194lamChain(K) = lamall{K+1}(z0);
195Lchain(:,K) = Ljkall{K+1}(:,z0);
196
197% Part 2: measures of the chains that visit at least one IS center.
198lmaxK = min(K-1,K-S);
199if D >= 2 && lmaxK >= K-D+1
200 L2prev = cell(1,K);
201 for k = (K-D+2):K
202 idxI = find(vsum == K-k); % v in I_k
203 L2cur = cell(1,K);
204 for l = (K-D+1):min(k-1,K-S)
205 acc = zeros(length(idxI),J);
206 for j = 1:J
207 if l == k-1
208 % base case of (6): L^{k-1}_{i,k-1} comes from (9)
209 prev = Ljkall{k}(:,succ(idxI,j))';
210 else
211 prev = L2prev{l}(:,succ(idxI,j))';
212 end
213 acc = acc + Ljkall{k+1}(j,idxI)' .* prev;
214 end
215 L2cur{l} = zeros(J,nv);
216 L2cur{l}(:,idxI) = acc';
217 end
218 if k == K
219 for l = (K-D+1):lmaxK
220 Lchain(:,l) = L2cur{l}(:,z0);
221 % Little's law at an IS center visited by chain l
222 jIS = find(a((J1+1):J,l) > 0,1) + J1;
223 lamChain(l) = Lchain(jIS,l) / a(jIS,l);
224 end
225 end
226 L2prev = L2cur;
227 end
228end
229
230% Re-executions of part 1: measures of the chains that visit only SSFR centers.
231% Interchanging the labels of chains K-l and K leaves chains 1,...,K-l-1 in
232% place, so the levels up to K-l-1 computed by the first execution stay valid
233% and the basic step can restart at k0 = K-l.
234perm = 1:K; % perm(k) is the original label of the chain now labelled k
235for l = 1:(S-1)
236 perm([K-l,K]) = perm([K,K-l]);
237 a(:,[K-l,K]) = a(:,[K,K-l]);
238 ak([K-l,K]) = ak([K,K-l]);
239 [Lall,Ljkall,lamall] = mvac_part1(K-l,K,J,J1,a,ak,Vlist,vsum,succ,Lall,Ljkall,lamall);
240 lamChain(perm(K)) = lamall{K+1}(z0);
241 Lchain(:,perm(K)) = Ljkall{K+1}(:,z0);
242end
243
244%% Expand the per-chain measures back to the per-class measures.
245% Every chain of a subset has the same measures, hence a class with N_r chains
246% in that subset carries N_r times the measures of the subset representative.
247for g = 1:D
248 kg = K-D+g; % original label of the representative of subset g
249 for r = posr(grpOfClass == gorder(g))
250 XN(r) = N(r) * lamChain(kg);
251 QN(ssfrIdx,r) = N(r) * Lchain(1:J1,kg);
252 UN(ssfrIdx,r) = XN(r) * L(ssfrIdx,r);
253 CN(ssfrIdx,r) = QN(ssfrIdx,r) / XN(r);
254 end
255end
256% An empty class has zero throughput and queue-length, so its residence time is
257% reported as the bare service demand, as in PFQN_MVA.
258CN(:,N == 0) = L(:,N == 0);
259end
260
261function [Lall,Ljkall,lamall] = mvac_part1(k0,K,J,J1,a,ak,Vlist,vsum,succ,Lall,Ljkall,lamall)
262% Part 1 of the basic step: evaluate (10), (9a)-(9b) and (7) for k = k0,...,K
263% over v in V_k = I_k u ... u I_K.
264for k = k0:K
265 idxV = find(vsum <= K-k);
266 Lp = Lall{k}; % L^{k-1}
267 Vv = Vlist(idxV,:);
268 Lpv = Lp(:,idxV)'; % L^{k-1}_j(v)
269 ajk = a(:,k)'; % a_jk
270 % (10): the chain-k customer is at some center, so sum_j L^k_{jk}(v) = 1
271 den = ak(k) + sum((Lpv(:,1:J1) + Vv(:,1:J1)) .* ajk(1:J1), 2);
272 lam = 1 ./ den;
273 Ljkv = zeros(length(idxV),J);
274 Ljkv(:,1:J1) = (lam .* (1 + Lpv(:,1:J1) + Vv(:,1:J1))) .* ajk(1:J1); % (9a)
275 Ljkv(:,(J1+1):J) = lam .* ajk((J1+1):J); % (9b)
276 % (7)
277 Lkv = Ljkv;
278 for j = 1:J
279 Lkv = Lkv + Ljkv(:,j) .* Lp(:,succ(idxV,j))';
280 end
281 Lk = zeros(J,length(vsum));
282 Lk(:,idxV) = Lkv';
283 Ljk = zeros(J,length(vsum));
284 Ljk(:,idxV) = Ljkv';
285 lamv = zeros(1,length(vsum));
286 lamv(idxV) = lam;
287 Lall{k+1} = Lk;
288 Ljkall{k+1} = Ljk;
289 lamall{k+1} = lamv;
290end
291end
Definition Station.m:245