LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
pfqn_mvaoi_marg.m
1function [XN, QN] = pfqn_mvaoi_marg(D, N, isDelay, mu)
2% [XN, QN] = PFQN_MVAOI_MARG(D, N, ISDELAY, MU)
3%
4% Exact marginal load-dependent MVA for a closed product-form network of
5% infinite-server (delay) and load-independent (single-server, product-form)
6% stations plus ANY number of order-independent (OI) stations. An OI station is
7% a class-dependent load-dependent server whose total service rate mu_i(n) is a
8% permutation-invariant function of the per-class count vector n. The network is
9% product-form and is solved exactly by the load-dependent MVA recursion that
10% carries, for EACH OI station i, its joint count-vector marginal distribution
11% pM_i(n | k):
12%
13% pM_i(n | k) = (1/mu_i(n)) * sum_r X_r(k) * pM_i(n - e_r | k - e_r), n ~= 0
14% pM_i(0 | k) = 1 - sum_{n ~= 0} pM_i(n | k)
15%
16% All OI-station marginals share the common per-class throughput X_r(k); the
17% recursion is exact per station because in product form
18% pM_i(n|k) = Phi_i(n) G_{-i}(k-n)/G(k) with X_r(k) = G(k-e_r)/G(k) and the
19% balanced-fairness identity Phi_i(n) = (1/mu_i(n)) sum_r Phi_i(n-e_r).
20%
21% Because the OI rate is class-dependent, the mean-value response-time formula is
22% not exact; instead the per-class throughput X_r(k) is closed at each population
23% level by population conservation
24%
25% X_r(k) * A_r(k) + sum_i QM_ir(k; X) = k_r, A_r(k) = sum_{i not OI} R_ir(k)
26%
27% where QM_ir(k) = sum_n n_r pM_i(n | k) is read off each OI station's exact
28% marginal. This yields Q, X matching the exact CTMC / normalizing-constant (NC)
29% results for any number of OI stations. This is the marginal-distribution
30% counterpart of PFQN_MVAOI (the mean-value CMVA form).
31%
32% Parameters:
33% D - (M x R) per-class service demand V_ir/rate_ir at every station.
34% Rows of OI stations are ignored (rate comes from MU).
35% N - (1 x R) closed population vector, finite.
36% isDelay - (1 x M) logical, true for infinite-server (delay) stations.
37% mu - (1 x M) cell; mu{i} is a function handle mu_i(n) returning the OI
38% total service rate for the per-class occupancy vector n (1 x R) at
39% OI station i, and [] for non-OI stations.
40%
41% Returns:
42% XN - (1 x R) per-class throughput X_r = G(N-e_r)/G(N).
43% QN - (M x R) per-class mean queue-length at every station.
44%
45% Reference:
46% Reiser, Lavenberg (1980). Mean-Value Analysis of Closed Multichain Queuing
47% Networks. JACM 27(2). Load-dependent extension: Bruell, Balbo, Afshari
48% (1984). OI stations: Casale, Comte, Dorsman (2026).
49%
50% Copyright (c) 2012-2026, Imperial College London
51% All rights reserved.
52
53M = size(D, 1);
54R = numel(N);
55N = round(N(:)');
56isDelay = logical(isDelay(:)');
57isOI = false(1, M);
58for i = 1:M
59 isOI(i) = ~isempty(mu{i});
60end
61oi_list = find(isOI);
62nOI = numel(oi_list);
63if nOI == 0
64 line_error(mfilename, 'pfqn_mvaoi_marg requires at least one order-independent station.');
65end
66
67% OI rank rate mu_i(n) per OI station via the (1-based) canonical microstate.
68muM = cell(1, nOI);
69for o = 1:nOI
70 svcRateFun = mu{oi_list(o)};
71 muM{o} = @(n) oi_rate(svcRateFun, n, R);
72end
73
74% Caches keyed by population-vector string. The OI marginals are carried per
75% station: pM_keys{o}/pM_vals{o} hold station oi_list(o)'s count-vector marginal.
76X_cache = containers.Map('KeyType','char','ValueType','any'); % X_r(k)
77Q_cache = containers.Map('KeyType','char','ValueType','any'); % Q_ir(k) (M x R)
78pM_keys = cell(1, nOI);
79pM_vals = cell(1, nOI);
80for o = 1:nOI
81 pM_keys{o} = containers.Map('KeyType','char','ValueType','any');
82 pM_vals{o} = containers.Map('KeyType','char','ValueType','any');
83end
84
85zeroKey = vec2key(zeros(1,R));
86X_cache(zeroKey) = zeros(1,R);
87Q_cache(zeroKey) = zeros(M,R);
88for o = 1:nOI
89 pM_keys{o}(zeroKey) = zeros(1,R);
90 pM_vals{o}(zeroKey) = 1.0;
91end
92
93pops = enum_vecs(N);
94[~, ord] = sortrows([sum(pops,2), pops]);
95pops = pops(ord, :);
96
97for pidx = 1:size(pops,1)
98 k = pops(pidx, :);
99 if sum(k) == 0
100 continue
101 end
102 kkey = vec2key(k);
103
104 % Fixed (non-OI) station response times via the arrival theorem at k - e_r.
105 Rfix = zeros(M, R);
106 A = zeros(1, R);
107 for r = 1:R
108 if k(r) == 0
109 continue
110 end
111 kr = k; kr(r) = kr(r) - 1;
112 Qkr = Q_cache(vec2key(kr));
113 for i = 1:M
114 if isOI(i)
115 continue
116 end
117 if isDelay(i)
118 Rfix(i,r) = D(i,r);
119 else
120 Rfix(i,r) = D(i,r) * (1 + sum(Qkr(i,:)));
121 end
122 A(r) = A(r) + Rfix(i,r);
123 end
124 end
125
126 % Close X(k) by population conservation: X_r*A_r + sum_i QM_ir(k;X) = k_r.
127 Xk = zeros(1,R);
128 for r = 1:R
129 if k(r) > 0
130 Xk(r) = k(r) / (A(r) + 1);
131 end
132 end
133 nkAll = cell(1, nOI); pvAll = cell(1, nOI);
134 for it = 1:2000
135 QMtot = zeros(1, R);
136 for o = 1:nOI
137 [nkAll{o}, pvAll{o}] = oi_marginal(k, Xk, muM{o}, R, pM_keys{o}, pM_vals{o});
138 QMtot = QMtot + sum(nkAll{o} .* pvAll{o}, 1);
139 end
140 Xnew = zeros(1,R);
141 for r = 1:R
142 if k(r) > 0 && A(r) > 0
143 Xnew(r) = max((k(r) - QMtot(r)) / A(r), 0);
144 end
145 end
146 if max(abs(Xnew - Xk)) < 1e-13
147 Xk = Xnew;
148 break
149 end
150 Xk = 0.5*Xk + 0.5*Xnew;
151 end
152
153 for o = 1:nOI
154 [nkAll{o}, pvAll{o}] = oi_marginal(k, Xk, muM{o}, R, pM_keys{o}, pM_vals{o});
155 end
156
157 Qk = zeros(M, R);
158 for o = 1:nOI
159 QMi = sum(nkAll{o} .* pvAll{o}, 1);
160 Qk(oi_list(o), :) = QMi;
161 end
162 for r = 1:R
163 for i = 1:M
164 if isOI(i)
165 continue
166 end
167 Qk(i,r) = Xk(r) * Rfix(i,r);
168 end
169 end
170 X_cache(kkey) = Xk;
171 Q_cache(kkey) = Qk;
172 for o = 1:nOI
173 pM_keys{o}(kkey) = nkAll{o};
174 pM_vals{o}(kkey) = pvAll{o};
175 end
176end
177
178Nkey = vec2key(N);
179XN = X_cache(Nkey);
180QN = Q_cache(Nkey);
181end
182
183% =========================================================================
184% Helper functions
185% =========================================================================
186
187function rate = oi_rate(svcRateFun, n, R)
188% OI total service rate at count vector n via the 1-based canonical microstate.
189if sum(n) == 0
190 rate = 0;
191 return
192end
193micro = repelem(1:R, n);
194rate = svcRateFun(micro);
195end
196
197function [nk, pv] = oi_marginal(k, Xk, muM, R, pM_keys, pM_vals)
198% Vector marginal pM(n | k) at the OI station given throughput Xk. Returns count
199% vectors nk (rows) and their probabilities pv (column), including n = 0.
200vecs = enum_vecs(k);
201nrows = size(vecs, 1);
202nk = zeros(nrows, R);
203pv = zeros(nrows, 1);
204cnt = 0;
205psum = 0;
206zeroRow = -1;
207for idx = 1:nrows
208 n = vecs(idx, :);
209 if sum(n) == 0
210 cnt = cnt + 1;
211 nk(cnt, :) = n;
212 pv(cnt) = 0; % filled after normalization
213 zeroRow = cnt;
214 continue
215 end
216 rate = muM(n);
217 if rate <= 0
218 continue
219 end
220 acc = 0;
221 for r = 1:R
222 if n(r) >= 1 && k(r) >= 1
223 nr = n; nr(r) = nr(r) - 1;
224 kr = k; kr(r) = kr(r) - 1;
225 acc = acc + Xk(r) * lookup_prob(pM_keys, pM_vals, kr, nr, R);
226 end
227 end
228 cnt = cnt + 1;
229 nk(cnt, :) = n;
230 pv(cnt) = acc / rate;
231 psum = psum + pv(cnt);
232end
233nk = nk(1:cnt, :);
234pv = pv(1:cnt);
235if zeroRow > 0
236 pv(zeroRow) = 1 - psum;
237end
238end
239
240function p = lookup_prob(pM_keys, pM_vals, kr, nr, R)
241% Probability pM(nr | kr) from the cached marginal at population kr.
242krkey = vec2key(kr);
243if ~isKey(pM_keys, krkey)
244 p = 0;
245 return
246end
247keysMat = pM_keys(krkey);
248valsVec = pM_vals(krkey);
249match = all(keysMat == repmat(nr, size(keysMat,1), 1), 2);
250row = find(match, 1);
251if isempty(row)
252 p = 0;
253else
254 p = valsVec(row);
255end
256end
257
258function vecs = enum_vecs(bound)
259% All integer vectors v with 0 <= v <= bound (rows), in ndgrid order.
260R = numel(bound);
261if R == 0
262 vecs = zeros(1,0);
263 return
264end
265ranges = cell(1, R);
266for r = 1:R
267 ranges{r} = 0:bound(r);
268end
269grids = cell(1, R);
270[grids{:}] = ndgrid(ranges{:});
271vecs = zeros(numel(grids{1}), R);
272for r = 1:R
273 vecs(:, r) = grids{r}(:);
274end
275end
276
277function key = vec2key(v)
278key = sprintf('%d_', round(v));
279end
Definition Station.m:245