4 % @brief MVAC (Mean Value Analysis by Chain)
for product-form queueing networks.
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).
21function [XN,QN,UN,CN] = pfqn_mvac(L,N,Z)
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.
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.
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.
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
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).
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).
84% See also PFQN_MVA, PFQN_RECAL, PFQN_CONV.
86% Copyright (c) 2012-2026, Imperial College London
90if nargin < 3 || isempty(Z)
94 line_error(mfilename,'
the think time matrix and
the demand matrix have a different number of classes');
98 line_error(mfilename,'
the demand matrix and
the population vector have a different number of classes');
100if any(N < 0) || any(isinf(N))
101 line_error(mfilename,'
the population vector must be finite and nonnegative');
109K = sum(N); % number of single-customer chains in
the original network
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))';
119J = J1 + length(isIdx);
121 line_error(mfilename,'all service demands are zero,
the throughput
is unbounded');
123% A(j,r) = a_jr, centers ordered SSFR first as required by (9a)-(9b) and (10)
124A = [L(ssfrIdx,:); Z(isIdx,:)];
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.
130[Adist,~,grpOfClass] = unique(A(:,posr)','rows','stable');
132visitsIS = false(1,Dall);
134 visitsIS(g) = any(Adist(g,(J1+1):J) > 0);
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)];
142Agrp = Adist(gorder,:)'; % (J x D) demands of
the representative of each subset
145 mult(g) = sum(N(posr(grpOfClass == gorder(g))));
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;
154 for c = 1:(mult(g)-1)
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
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.
169 Vlist = [Vlist; multichoose(J,t)]; %#ok<AGROW>
173% succ(vi,j)
is the index of v + 1_j, or 0 when out of range (sum(v) == K)
177 Vplus(:,j) = Vplus(:,j) + 1;
178 [tf,loc] = ismember(Vplus,Vlist,
'rows');
179 succ(:,j) = loc .* tf;
181z0 = find(vsum == 0,1); % index of v = 0
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
189lamChain = zeros(1,K); % per-chain throughput, indexed by
the ORIGINAL chain label
190Lchain = zeros(J,K); % per-chain mean queue-length, same indexing
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);
197% Part 2: measures of
the chains that visit at least one IS center.
199if D >= 2 && lmaxK >= K-D+1
202 idxI = find(vsum == K-k); % v in I_k
204 for l = (K-D+1):min(k-1,K-S)
205 acc = zeros(length(idxI),J);
208 % base
case of (6): L^{k-1}_{i,k-1} comes from (9)
209 prev = Ljkall{k}(:,succ(idxI,j))
';
211 prev = L2prev{l}(:,succ(idxI,j))';
213 acc = acc + Ljkall{k+1}(j,idxI)
' .* prev;
215 L2cur{l} = zeros(J,nv);
216 L2cur{l}(:,idxI) = acc';
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);
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
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);
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.
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);
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);
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.
265 idxV = find(vsum <= K-k);
266 Lp = Lall{k}; % L^{k-1}
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);
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)
279 Lkv = Lkv + Ljkv(:,j) .* Lp(:,succ(idxV,j))';
281 Lk = zeros(J,length(vsum));
283 Ljk = zeros(J,length(vsum));
285 lamv = zeros(1,length(vsum));