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)
4% Smith Queue Decomposition (SQD): approximate MVA for closed Blocking-After-Service (BAS) networks.
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.
14% Returns per-station throughput X, queue length Q, utilization U, residence time R.
16% Originally contributed as SolverDBT by Avinash Bommareddy (Imperial College London
17% FYP, 2026); refactored here into an sn-based API function.
19% Copyright (c) 2012-2026, Imperial College London
23CALIBRATION_EPSILON = 0.05;
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
35[~,STchain,Vchain] = sn_get_demands_chain(sn);
44 isDelay(i) = (s == SchedStrategy.INF || s == SchedStrategy.EXT);
49 if isinf(c) || c > 1e14
57pEff = computeEffectiveRouting(sn, M, Rcl, isDelay);
65 v1init(i) = INITIAL_V1;
67 v1init(i) = initialV1(i);
76ownserver = strcmpi(neighborMode,
'ownserver');
77fresh = strcmpi(v1Policy,
'fresh');
91 pBlockDown = mm1kBlocking(cap(i), X * V(i) * ST(i));
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);
110 n = n + pEff(i,j) * L_svr(j);
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
122 if mu_n < 1e-10, mu_n = 1e-10; end
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
127 if serverBlockingTime
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);
135 theta = ST(j) / denom;
139 bt = bt + pEff(i,j) * pBj * ST(j) * theta;
142 W_svr(i) = W_svr(i) + bt;
147 sumVW = sum(V .* (W_buf + W_svr));
155 L_buf = X .* V .* W_buf;
156 L_svr = X .* V .* W_svr;
161 if isDelay(i),
continue; end
164 pBlock = mm1kBlocking(cap(i), X * V(i) * ST(i));
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);
178 V1(i) = v1init(i) * (1.0 - pBlock);
180 V1(i) = V1(i) * (1.0 - pBlock);
182 if V1(i) < 1e-10, V1(i) = 1e-10; end
193 Q_i = L_buf(i) + L_svr(i);
194 W_tot = W_buf(i) + W_svr(i);
200 U_i = min(1.0, T_i * ST(i));
214function [beta, gamma] = computeBetaGamma(K, pBlockDown, mode, CALIBRATION_EPSILON)
215% Calibrate (beta, gamma) of
the load-dependent effective service rate.
217 beta = K; gamma = 1.0;
return;
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);
228 case 1 % fixed heuristic
230 Vb = CALIBRATION_EPSILON;
231 otherwise % mode 0: base
232 beta = K; gamma = 1.0;
return;
239 beta = K; gamma = 1.0;
return;
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);
248if ~isfinite(gamma) || ~isfinite(beta) || beta <= 0
249 beta = K; gamma = 1.0;
return;
253function p = computeEffectiveRouting(sn, M, Rcl, isDelay)
254% Build station-to-station effective routing, collapsing pass-through delay
nodes.
257 if isDelay(i),
continue; end
258 sf_i = sn.stationToStateful(i);
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
264 p(i,j) = p(i,j) + p_ij;
268 sf_k = sn.stationToStateful(k);
269 p_jk = sn.rt((sf_j - 1) * Rcl + 1, (sf_k - 1) * Rcl + 1);
271 p(i,k) = p(i,k) + p_ij * p_jk;
280function pb = mm1kBlocking(K, rho)
281% Steady-state blocking probability of an M/M/1/K queue at load rho.
284elseif abs(rho - 1.0) < 1e-9
285 pb = 1.0 / (K + 1.0);
287 pb = (1.0 - rho) * rho ^ K / (1.0 - rho ^ (K + 1));