LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
npfqn_sqd.m
1function [X,Q,U,R] = npfqn_sqd(sn, N, calibrationMode, serverBlockingTime, neighborMode, v1Policy, initialV1)
2% [X,Q,U,R] = NPFQN_BAS(SN, N, CALIBRATIONMODE, SERVERBLOCKINGTIME, NEIGHBORMODE, V1POLICY, INITIALV1)
3%
4% Smith Queue Decomposition (SQD): approximate MVA for closed Blocking-After-Service (BAS) networks.
5%
6% Solves a finite-buffer closed queueing network under Blocking-After-Service
7% (manufacturing/transfer) blocking directly from its NetworkStruct. The method is an
8% AMVA-style population recursion in which each finite-capacity station is described by
9% a load-dependent effective service rate calibrated from an M/M/1/K blocking
10% probability; downstream blocking is propagated through the effective routing between
11% service stations. Delay (INF/EXT) stations are treated as infinite-capacity pure-delay
12% nodes. Single-chain (chain-aggregated) demands only.
13%
14% Returns per-station throughput X, queue length Q, utilization U, residence time R.
15%
16% Originally contributed as SolverDBT by Avinash Bommareddy (Imperial College London
17% FYP, 2026); refactored here into an sn-based API function.
18%
19% Copyright (c) 2012-2026, Imperial College London
20% All rights reserved.
21
22INITIAL_V1 = 692.192;
23CALIBRATION_EPSILON = 0.05;
24
25M = sn.nstations;
26Rcl = sn.nclasses;
27
28if nargin < 2 || isempty(N), N = sn.nclosedjobs; end
29if nargin < 3 || isempty(calibrationMode), calibrationMode = 0; end
30if nargin < 4 || isempty(serverBlockingTime), serverBlockingTime = true; end
31if nargin < 5 || isempty(neighborMode), neighborMode = 'downstream'; end
32if nargin < 6 || isempty(v1Policy), v1Policy = 'compound'; end
33if nargin < 7, initialV1 = []; end
34
35[~,STchain,Vchain] = sn_get_demands_chain(sn);
36
37V = Vchain(:,1);
38ST = STchain(:,1);
39
40isDelay = false(M,1);
41cap = zeros(M,1);
42for i = 1:M
43 s = sn.sched(i);
44 isDelay(i) = (s == SchedStrategy.INF || s == SchedStrategy.EXT);
45 if isDelay(i)
46 cap(i) = Inf;
47 else
48 c = sn.cap(i);
49 if isinf(c) || c > 1e14
50 cap(i) = Inf;
51 else
52 cap(i) = c;
53 end
54 end
55end
56
57pEff = computeEffectiveRouting(sn, M, Rcl, isDelay);
58
59V1 = zeros(M,1);
60v1init = zeros(M,1);
61L_buf = zeros(M,1);
62L_svr = zeros(M,1);
63for i = 1:M
64 if isempty(initialV1)
65 v1init(i) = INITIAL_V1;
66 else
67 v1init(i) = initialV1(i);
68 end
69 V1(i) = v1init(i);
70end
71
72X = 0.0;
73W_buf = zeros(M,1);
74W_svr = zeros(M,1);
75
76ownserver = strcmpi(neighborMode, 'ownserver');
77fresh = strcmpi(v1Policy, 'fresh');
78
79for pop = 1:N
80
81 % wait times
82 for i = 1:M
83 if isDelay(i)
84 W_buf(i) = 0.0;
85 W_svr(i) = ST(i);
86 continue;
87 end
88
89 if ownserver
90 if ~isinf(cap(i))
91 pBlockDown = mm1kBlocking(cap(i), X * V(i) * ST(i));
92 else
93 pBlockDown = 0.0;
94 end
95 else
96 pBlockDown = 0.0;
97 for j = 1:M
98 if ~isDelay(j) && pEff(i,j) > 0 && ~isinf(cap(j))
99 rho_j = X * V(j) * ST(j);
100 pBlockDown = pBlockDown + pEff(i,j) * mm1kBlocking(cap(j), rho_j);
101 end
102 end
103 end
104
105 if ownserver
106 n = L_svr(i);
107 else
108 n = 0.0;
109 for j = 1:M
110 n = n + pEff(i,j) * L_svr(j);
111 end
112 end
113
114 if n < 1e-10
115 mu_n = V1(i);
116 else
117 [beta, gamma] = computeBetaGamma(cap(i), pBlockDown, calibrationMode, CALIBRATION_EPSILON);
118 base = max(0.0, (n - 1.0) / beta);
119 expArg = base ^ gamma;
120 mu_n = n * V1(i) * exp(-expArg); % Eq.13
121 end
122 if mu_n < 1e-10, mu_n = 1e-10; end
123
124 W_buf(i) = (1.0 / mu_n) * (1.0 + n); % Eq.18
125 W_svr(i) = ST(i) * (1.0 + L_svr(i)); % Eq.17
126
127 if serverBlockingTime
128 bt = 0.0;
129 for j = 1:M
130 if ~isDelay(j) && pEff(i,j) > 0 && ~isinf(cap(j))
131 rho_j = X * V(j) * ST(j);
132 pBj = mm1kBlocking(cap(j), rho_j);
133 denom = ST(i) + ST(j);
134 if denom > 1e-15
135 theta = ST(j) / denom;
136 else
137 theta = 0.0;
138 end
139 bt = bt + pEff(i,j) * pBj * ST(j) * theta;
140 end
141 end
142 W_svr(i) = W_svr(i) + bt;
143 end
144 end
145
146 % throughput
147 sumVW = sum(V .* (W_buf + W_svr));
148 if sumVW > 1e-15
149 X = pop / sumVW;
150 else
151 X = 0.0;
152 end
153
154 % queue lengths
155 L_buf = X .* V .* W_buf;
156 L_svr = X .* V .* W_svr;
157
158 % adjust V1
159 if pop < N
160 for i = 1:M
161 if isDelay(i), continue; end
162 if ownserver
163 if ~isinf(cap(i))
164 pBlock = mm1kBlocking(cap(i), X * V(i) * ST(i));
165 else
166 pBlock = 0.0;
167 end
168 else
169 pBlock = 0.0;
170 for j = 1:M
171 if ~isDelay(j) && pEff(i,j) > 0 && ~isinf(cap(j))
172 rho_j = X * V(j) * ST(j);
173 pBlock = pBlock + pEff(i,j) * mm1kBlocking(cap(j), rho_j);
174 end
175 end
176 end
177 if fresh
178 V1(i) = v1init(i) * (1.0 - pBlock);
179 else
180 V1(i) = V1(i) * (1.0 - pBlock);
181 end
182 if V1(i) < 1e-10, V1(i) = 1e-10; end
183 end
184 end
185end
186
187XN = zeros(M,1);
188QN = zeros(M,1);
189UN = zeros(M,1);
190WN = zeros(M,1);
191for i = 1:M
192 T_i = X * V(i);
193 Q_i = L_buf(i) + L_svr(i);
194 W_tot = W_buf(i) + W_svr(i);
195 if T_i > 1e-15
196 R_i = Q_i / T_i;
197 else
198 R_i = W_tot;
199 end
200 U_i = min(1.0, T_i * ST(i));
201 XN(i) = T_i;
202 QN(i) = Q_i;
203 UN(i) = U_i;
204 WN(i) = R_i;
205end
206
207X = XN;
208Q = QN;
209U = UN;
210R = WN;
211
212end
213
214function [beta, gamma] = computeBetaGamma(K, pBlockDown, mode, CALIBRATION_EPSILON)
215% Calibrate (beta, gamma) of the load-dependent effective service rate.
216if K <= 2 || isinf(K)
217 beta = K; gamma = 1.0; return;
218end
219
220a = 2.0;
221b = K;
222
223switch mode
224 case 2 % blocking-aware
225 pK = max(1e-6, min(pBlockDown, 1.0 - 1e-6));
226 Va = 1.0 - pK * (a - 1.0) / (b - 1.0);
227 Vb = 1.0 - pK;
228 case 1 % fixed heuristic
229 Va = (b - a) / b;
230 Vb = CALIBRATION_EPSILON;
231 otherwise % mode 0: base
232 beta = K; gamma = 1.0; return;
233end
234
235Va = min(Va, 0.999);
236Va = max(Va, 0.01);
237Vb = max(Vb, 1e-6);
238if Vb >= Va
239 beta = K; gamma = 1.0; return;
240end
241
242lnVa = log(Va);
243lnVb = log(Vb);
244gamma = log(lnVa / lnVb) / log((a - 1.0) / (b - 1.0));
245gamma = max(0.5, min(gamma, 10.0));
246beta = (a - 1.0) / (-lnVa) ^ (1.0 / gamma);
247
248if ~isfinite(gamma) || ~isfinite(beta) || beta <= 0
249 beta = K; gamma = 1.0; return;
250end
251end
252
253function p = computeEffectiveRouting(sn, M, Rcl, isDelay)
254% Build station-to-station effective routing, collapsing pass-through delay nodes.
255p = zeros(M, M);
256for i = 1:M
257 if isDelay(i), continue; end
258 sf_i = sn.stationToStateful(i);
259 for j = 1:M
260 sf_j = sn.stationToStateful(j);
261 p_ij = sn.rt((sf_i - 1) * Rcl + 1, (sf_j - 1) * Rcl + 1);
262 if p_ij <= 0, continue; end
263 if ~isDelay(j)
264 p(i,j) = p(i,j) + p_ij;
265 else
266 for k = 1:M
267 if ~isDelay(k)
268 sf_k = sn.stationToStateful(k);
269 p_jk = sn.rt((sf_j - 1) * Rcl + 1, (sf_k - 1) * Rcl + 1);
270 if p_jk > 0
271 p(i,k) = p(i,k) + p_ij * p_jk;
272 end
273 end
274 end
275 end
276 end
277end
278end
279
280function pb = mm1kBlocking(K, rho)
281% Steady-state blocking probability of an M/M/1/K queue at load rho.
282if rho <= 1e-15
283 pb = 0.0;
284elseif abs(rho - 1.0) < 1e-9
285 pb = 1.0 / (K + 1.0);
286else
287 pb = (1.0 - rho) * rho ^ K / (1.0 - rho ^ (K + 1));
288end
289end
Definition fjtag.m:157
Definition Station.m:245