LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
pfqn_schmidt_ext.m
1%{
2%{
3 % @file pfqn_schmidt_ext.m
4 % @brief Extended Schmidt MVA algorithm with queue-aware alpha corrections.
5%}
6%}
7
8%{
9%{
10 % @brief Extended Schmidt MVA algorithm with queue-aware alpha corrections.
11 % @fn pfqn_schmidt_ext(D, N, S, sched)
12 % @param D Service demand matrix (M x R).
13 % @param N Population vector (1 x R).
14 % @param S Number of servers per station (M x 1).
15 % @param sched Scheduling discipline per station.
16 % @return XN System throughput.
17 % @return QN Mean queue lengths.
18 % @return UN Utilization.
19 % @return CN Cycle times.
20 % @return T Results table.
21%}
22%}
23function [XN,QN,UN,CN,T] = pfqn_schmidt_ext(D,N,S,sched)
24% [XN,QN,UN,CN,T] = PFQN_SCHMIDT_EXT(D,N,S,SCHED)
25%
26% Extended Schmidt MVA algorithm with queue-aware alpha corrections.
27% A queue-aware version of the Schmidt algorithm that precomputes alpha values
28% for improved accuracy in networks with class-dependent FCFS scheduling.
29%
30% Reference: R. Schmidt, "An approximate MVA algorithm for exponential,
31% class-dependent multiple server stations," Performance Evaluation,
32% vol. 29, no. 4, pp. 245-254, 1997.
33%
34% Copyright (c) 2012-2026, Imperial College London
35% All rights reserved.
36
37[M, R] = size(D);
38closedClasses = 1:R;
39XN = zeros(1, R);
40UN = zeros(M, R);
41CN = zeros(M, R);
42QN = zeros(M, R);
43
44C = length(closedClasses);
45Dc = D(:, closedClasses);
46Nc = N(closedClasses);
47
48% Precompute alphas for FCFS stations
49alphas = cell(M, R);
50for i = 1:M
51 if sched(i) == SchedStrategy.FCFS
52 % Check if class-dependent
53 classIndependent = true;
54 for r = 2:R
55 if D(i, r) ~= D(i, 1)
56 classIndependent = false;
57 break
58 end
59 end
60
61 if ~classIndependent
62 for r = 1:R
63 % Create modified problem with tagged customer
64 D_mod = zeros(M, R + 1);
65 N_mod = zeros(1, R + 1);
66
67 for k = 1:R
68 if k == r
69 N_mod(k) = N(k) - 1;
70 else
71 N_mod(k) = N(k);
72 end
73 for j = 1:M
74 D_mod(j, k) = D(j, k);
75 if i == j
76 D_mod(j, R + 1) = D(j, r);
77 else
78 D_mod(j, R + 1) = 0;
79 end
80 end
81 end
82 N_mod(R + 1) = 1;
83
84 % Create extended sched array
85 sched_mod = [sched(:)', SchedStrategy.FCFS];
86
87 % Run basic Schmidt to get alpha values
88 [~, ~, U_alpha, ~, ~] = pfqn_schmidt(D_mod, N_mod, S, sched_mod);
89 alphas{i, r} = U_alpha;
90 end
91 end
92 end
93end
94
95% Compute products for hashing
96prods = zeros(1, C);
97for r = 1:C
98 prods(r) = prod(Nc(1:r-1) + 1);
99end
100
101% Start at nc=(0,...,0)
102kvec = pprod(Nc);
103
104% Initialize L and Pc
105L = cell(1, M);
106Pc = cell(1, M);
107for ist = 1:M
108 % Always initialize L for ALL stations (queue lengths needed everywhere)
109 L{ist} = zeros(R, prod(1 + Nc));
110 switch sched(ist)
111 case SchedStrategy.INF
112 % No Pc needed for infinite server
113 case SchedStrategy.PS
114 if ~all(S(ist, :) == 1)
115 % Multi-server PS needs state probabilities
116 Pc{ist} = zeros(1 + sum(Nc), prod(1 + Nc));
117 end
118 case SchedStrategy.FCFS
119 classIndependent = all(D(ist, :) == D(ist, 1));
120 if classIndependent
121 if ~all(S(ist, :) == 1)
122 Pc{ist} = zeros(1 + sum(Nc), prod(1 + Nc));
123 end
124 else
125 % Class-dependent needs vector probabilities
126 Pc{ist} = zeros(prod(1 + Nc), prod(1 + Nc));
127 end
128 end
129end
130
131x = zeros(C, prod(1 + Nc));
132w = zeros(M, C, prod(1 + Nc));
133
134for ist = 1:M
135 if ~isempty(Pc{ist})
136 Pc{ist}(1 + 0, hashpop(kvec, Nc, C, prods)) = 1.0;
137 end
138end
139
140% Population recursion
141while all(kvec >= 0) && all(kvec <= Nc)
142 kprods = zeros(1, C);
143 for r = 1:C
144 kprods(r) = prod(kvec(1:r-1) + 1);
145 end
146
147 for ist = 1:M
148 for c = 1:C
149 hkvec = hashpop(kvec, Nc, C, prods);
150 hkvec_c = hashpop(oner(kvec, c), Nc, C, prods);
151
152 if length(S(ist, :)) == 1
153 ns = S(ist);
154 else
155 ns = S(ist, c);
156 end
157
158 if kvec(c) > 0
159 switch sched(ist)
160 case SchedStrategy.INF
161 w(ist, c, hkvec) = D(ist, c);
162
163 case SchedStrategy.PS
164 if ns == 1
165 % Sum queue lengths of ALL classes (arrival theorem)
166 totalQueueLength = sum(L{ist}(:, hkvec_c));
167 w(ist, c, hkvec) = Dc(ist, c) * (1 + totalQueueLength);
168 else
169 % Sum queue lengths of ALL classes (arrival theorem)
170 totalQueueLength = sum(L{ist}(:, hkvec_c));
171 w(ist, c, hkvec) = (Dc(ist, c) / ns) * (1 + totalQueueLength);
172 for j = 1:ns-1
173 % Pc{ist}(j,...) = Pr(j-1 jobs) due to MATLAB 1-indexing
174 w(ist, c, hkvec) = w(ist, c, hkvec) + (ns - 1 - (j - 1)) * Pc{ist}(j, hkvec_c) * (Dc(ist, c) / ns);
175 end
176 end
177
178 case SchedStrategy.FCFS
179 classIndependent = all(D(ist, :) == D(ist, 1));
180
181 if ~classIndependent
182 if ns == 1
183 % Sum queue lengths of ALL classes (arrival theorem)
184 totalQueueLength = sum(L{ist}(:, hkvec_c));
185 w(ist, c, hkvec) = Dc(ist, c) * (1 + totalQueueLength);
186 else
187 nvec = pprod(kvec);
188 while all(nvec >= 0)
189 if nvec(c) > 0
190 % Use Nc and prods for hash (matches Java)
191 hnvec_c = hashpop(oner(nvec, c), Nc, C, prods);
192
193 % Use extended Bcn with alpha
194 if Nc(c) > 1 && ~isempty(alphas{ist, c})
195 Bcn = getBcnExt(alphas{ist, c}, D, ist, c, nvec, C, ns, R);
196 else
197 Bcn = getBcn(D, ist, c, nvec, C, ns);
198 end
199
200 w(ist, c, hkvec) = w(ist, c, hkvec) + Bcn * Pc{ist}(hnvec_c, hkvec_c);
201 end
202 nvec = pprod(nvec, kvec);
203 end
204 end
205 else
206 % Class-independent: treat like PS
207 if ns == 1
208 % Sum queue lengths of ALL classes (arrival theorem)
209 totalQueueLength = sum(L{ist}(:, hkvec_c));
210 w(ist, c, hkvec) = Dc(ist, c) * (1 + totalQueueLength);
211 else
212 % Sum queue lengths of ALL classes (arrival theorem)
213 totalQueueLength = sum(L{ist}(:, hkvec_c));
214 w(ist, c, hkvec) = (Dc(ist, c) / ns) * (1 + totalQueueLength);
215 for j = 1:ns-1
216 % Pc{ist}(j,...) = Pr(j-1 jobs) due to MATLAB 1-indexing
217 w(ist, c, hkvec) = w(ist, c, hkvec) + (ns - 1 - (j - 1)) * Pc{ist}(j, hkvec_c) * (Dc(ist, c) / ns);
218 end
219 end
220 end
221 end
222 end
223 end
224 end
225
226 % Compute throughputs
227 for c = 1:C
228 sumW = sum(w(1:M, c, hkvec));
229 if sumW > 0
230 x(c, hkvec) = kvec(c) / sumW;
231 else
232 x(c, hkvec) = 0;
233 end
234 end
235 x(isnan(x)) = 0;
236
237 % Update queue lengths
238 for ist = 1:M
239 for c = 1:C
240 L{ist}(c, hkvec) = x(c, hkvec) * w(ist, c, hkvec);
241 end
242
243 if length(S(ist, :)) == 1
244 ns = S(ist);
245 else
246 ns = S(ist, c);
247 end
248
249 switch sched(ist)
250 case SchedStrategy.PS
251 if ns > 1
252 for n = 1:min(S(ist), sum(kvec))
253 for c = 1:C
254 if kvec(c) > 0
255 hkvec_c = hashpop(oner(kvec, c), Nc, C, prods);
256 Pc{ist}(1 + n, hkvec) = Pc{ist}(1 + n, hkvec) + Dc(ist, c) * (1/n) * x(c, hkvec) * Pc{ist}(1 + (n - 1), hkvec_c);
257 end
258 end
259 Pc{ist}(1 + 0, hkvec) = max(eps, 1 - sum(Pc{ist}(1 + (1:min(S(ist), sum(kvec))), hkvec)));
260 end
261 end
262
263 case SchedStrategy.FCFS
264 classIndependent = all(D(ist, :) == D(ist, 1));
265
266 if ~classIndependent
267 nvec = pprod(kvec);
268 nvec = pprod(nvec, kvec);
269 sumOfAllProbs = 0;
270 while all(nvec >= 0)
271 % Use Nc and prods for hash (matches Java)
272 hnvec = hashpop(nvec, Nc, C, prods);
273
274 prob = 0;
275 for r = 1:C
276 if nvec(r) > 0
277 % Use Nc and prods for hash (matches Java)
278 hnvec_c = hashpop(oner(nvec, r), Nc, C, prods);
279 hkvec_c = hashpop(oner(kvec, r), Nc, C, prods);
280
281 if Nc(r) > 1 && ~isempty(alphas{ist, r})
282 Bcn = getBcnExt(alphas{ist, r}, D, ist, r, nvec, C, ns, R);
283 else
284 Bcn = getBcn(D, ist, r, nvec, C, ns);
285 end
286
287 capacity_inv = 1 / sum(nvec);
288 x_ir = x(r, hkvec);
289 prob_c = Pc{ist}(hnvec_c, hkvec_c);
290 classProb = Bcn * capacity_inv * x_ir * prob_c;
291 prob = prob + classProb;
292 end
293 end
294 Pc{ist}(hnvec, hkvec) = prob;
295 sumOfAllProbs = sumOfAllProbs + prob;
296 nvec = pprod(nvec, kvec);
297 end
298 Pc{ist}(1 + 0, hkvec) = max(1e-12, 1 - sumOfAllProbs);
299 else
300 % Class-independent multi-server
301 if ns > 1
302 K_j = 0;
303 for r = 1:R
304 % Assuming all classes visit if D > 0
305 if D(ist, r) > 0
306 K_j = K_j + Nc(r);
307 end
308 end
309
310 meanQueueLength = 0;
311 for r = 1:R
312 meanQueueLength = meanQueueLength + L{ist}(r, hkvec);
313 end
314
315 for n = 1:(min(ns, sum(kvec))-1)
316 if K_j > 0 && n <= K_j
317 frac = meanQueueLength / K_j;
318 x1 = nchoosek(K_j, n);
319 x2 = frac^n;
320 x3 = (1 - frac)^(K_j - n);
321 prob = x1 * x2 * x3;
322 Pc{ist}(1 + n, hkvec) = prob;
323 end
324 end
325
326 sum1 = 0;
327 sum2 = 0;
328 for r = 1:R
329 sum1 = sum1 + D(ist, r) * x(r, hkvec);
330 end
331 for n = 0:(ns-1)
332 sum2 = sum2 + (ns - n) * Pc{ist}(1 + n, hkvec);
333 end
334 term = (sum1 + sum2) / ns;
335 Pc{ist}(1 + 0, hkvec) = max(1e-12, 1 - term);
336 end
337 end
338 end
339 end
340
341 kvec = pprod(kvec, Nc);
342end
343
344% Extract final results
345hkvecFinal = hkvec;
346
347% Throughput
348XN(closedClasses) = x(1:C, hkvecFinal);
349if M > 1
350 XN = repmat(XN, M, 1);
351end
352
353% Utilization
354for m = 1:M
355 for c = 1:C
356 UN(m, c) = (D(m, c) * XN(c)) / S(m);
357 end
358end
359
360% Response time
361CN(1:M, closedClasses) = w(1:M, 1:C, hkvecFinal);
362
363% Queue length
364for ist = 1:M
365 QN(ist, closedClasses) = L{ist}(closedClasses, hkvecFinal);
366end
367
368T = table(XN, CN, QN, UN);
369
370end
371
372function Bcn = getBcn(D, i, c, nvec, C, ns)
373% Standard Bcn calculation
374Bcn = D(i, c);
375if sum(nvec) > 1
376 eps_val = 1e-12;
377 sumVal = 0;
378 for t = 1:C
379 sumVal = sumVal + nvec(t) * D(i, t);
380 end
381 Bcn = Bcn + (max(0, sum(nvec) - ns) / max(ns * (sum(nvec) - 1), eps_val) * (sumVal - D(i, c)));
382end
383end
384
385function Bcn = getBcnExt(u, D, i, c, nvec, C, ns, R)
386% Extended Bcn with alpha corrections
387weightedProb = 0;
388
389% u is the utilization matrix from the alpha computation (M x R+1)
390% The last column (R+1) is the tagged customer utilization
391totalNonPinnedTime = sum(u(i, 1:C)) - u(i, C + 1);
392
393if totalNonPinnedTime > 0
394 for s = 1:C
395 if D(i, s) > 0
396 prob = u(i, s) / totalNonPinnedTime;
397 weightedProb = weightedProb + (prob / D(i, s));
398 end
399 end
400end
401
402if weightedProb > 0
403 meanInterdepartureTime = 1.0 / (ns * weightedProb);
404else
405 meanInterdepartureTime = 0;
406end
407
408Bcn = D(i, c);
409
410if sum(nvec) > 1
411 Bcn = Bcn + (max(0, sum(nvec) - ns) * meanInterdepartureTime);
412end
413
414if isnan(Bcn) || isinf(Bcn)
415 Bcn = 0;
416end
417end