1function [Q,U,R,T,C,X,lG,totiter] = solver_rqna(sn, options)
2% [Q,U,R,T,C,X,lG,totiter] = SOLVER_RQNA(SN, OPTIONS)
4% Robust Queueing Network Analyzer (RQNA) based on indices of dispersion.
5% Approximates
the steady-state performance of a single-
class open queueing
6% network of single-server FCFS queues with Markovian routing and general
7% (non-renewal) external arrival and (non-exponential) service processes.
9% Reference: W. Whitt and W. You (2018),
"A Robust Queueing Network Analyzer
10% Based on Indices of Dispersion", INFORMS J. on Computing. This routine
11% implements Algorithm 1 (general framework): traffic-rate equations, limiting
12% variability equations, time-dependent IDC equations with default correction
13% terms alpha (eq. 34) and beta (eqs. 38-39), and
the robust-queueing (RQ)
14% workload approximation (eq. 13) with
the derived performance measures.
16% Copyright (c) 2012-2026, Imperial College London
20 line_error(mfilename,
'RQNA supports single-class open networks only. Use the ''qna'' method for multiclass models.');
22if any(isfinite(sn.njobs))
23 line_error(mfilename, 'RQNA supports open networks only (no closed classes).');
27K = sn.nclasses; % == 1
29Q = zeros(M,K); U = zeros(M,K); R = zeros(M,K); T = zeros(M,K);
30X = zeros(1,K); lG = NaN; totiter = 1;
32% ----- identify source and queueing stations -----
36 nd = sn.stationToNode(i);
37 isSource(i) = (sn.nodetype(nd) == NodeType.Source);
38 schedInf(i) = (sn.sched(i) == SchedStrategy.INF);
40srcList = find(isSource);
41qstat = find(~isSource); % queueing (and delay) stations
44% single-class station-to-station routing matrix
45rtS = full(sn.rt); % (nstateful x nstateful) for single class
47% ----- external arrival process (from
the source) -----
49 line_error(mfilename, 'RQNA requires an open network with a Source station.');
52arvMAP = sn.proc{src,1}{1};
53lambda_src = map_lambda(arvMAP);
54c2_src = map_idc(arvMAP);
55arvIdc = @(t) map_count_idc(arvMAP, t);
57% ----- per-queue data -----
58mu = zeros(nq,1); cs2 = zeros(nq,1);
59lambda0 = zeros(nq,1); c2a0 = zeros(nq,1);
61qsplit = zeros(nq,1); % source split prob into each queue
65 mu(a) = sn.rates(ia,1);
66 cs2(a) = sn.scv(ia,1);
67 svcMAP{a} = sn.proc{ia,1}{1};
68 qsplit(a) = rtS(src, ia);
69 lambda0(a) = lambda_src * qsplit(a);
76% external arrival IDC seen by each queue = split of
the source process (eq. 31)
77% I_{a,0,b}(t) = q_b I_src(t) + (1-q_b); c2_{a,0,b} = q_b c2_src + (1-q_b)
78c2a0 = qsplit .* c2_src + (1 - qsplit);
79a0IdcFun = @(t) qsplit .* arvIdc(t) + (1 - qsplit);
81% service IDC vector-valued function I_{s,a}(t)
82sIdcFun = @(t) local_svc_idc(svcMAP, t);
84% ----- traffic variability equations (limiting + time-dependent) -----
85corrections =
struct();
86if isfield(options,
'config') && isstruct(options.config)
87 if isfield(options.config,'rqna_alpha'), corrections.alpha = options.config.rqna_alpha; end
88 if isfield(options.config,'rqna_beta'), corrections.beta = options.config.rqna_beta; end
90ctx = npfqn_traffic_idc(lambda0,
P, c2a0, a0IdcFun, mu, cs2, sIdcFun, corrections);
94% near-immediate feedback elimination
is applied by default (Whitt-You
95% Algorithm 2 / Section 4.2); set options.config.rqna_feedback_elim=false for
96%
the plain RQNA (Algorithm 1) without elimination.
98if isfield(options,'config') && isstruct(options.config) && isfield(options.config,'rqna_feedback_elim')
99 doElim = logical(options.config.rqna_feedback_elim);
102% ----- per-queue robust-queueing workload and performance -----
110 % infinite-server (delay) station: no waiting
111 U(ia,1) = lambda(a)/mu(a);
112 Q(ia,1) = lambda(a)/mu(a);
118 phat = local_phat(
P, rho, a);
120 if phat > options.tol
121 % ----- near-immediate feedback elimination at station a -----
122 Ra = local_elim_response(a,
P, rho, lambda, mu, cs2, svcMAP, ...
123 lambda0, arvMAP, qsplit, corrections);
126 IaFun_a = @(x) local_component(ctx, x, a);
127 [~, Wa] = qsys_gig1_rq(rho(a), mu(a), cs2(a), IaFun_a);
128 R(ia,1) = Wa + 1/mu(a); % per-visit response time = waiting + service
131 Q(ia,1) = lambda(a) * R(ia,1);% mean number in system (Little, incl. service)
134% source station bookkeeping
135T(src,1) = lambda_src;
139Q(isnan(Q)) = 0; U(isnan(U)) = 0; R(isnan(R)) = 0; C(isnan(C)) = 0;
142function phat = local_phat(
P, rho, a)
143% Near-immediate feedback probability at station a:
the probability that a
144% customer departing a returns to a before visiting any station with strictly
145% higher traffic intensity (Whitt-You [52] eq. 3.8/3.9, H={a}).
146Hc = setdiff(find(rho <= rho(a) + 1e-9), a); % equal/lower-rho cloud
151F = inv(eye(numel(Hc)) -
P(Hc,Hc));
152phat =
P(a,a) +
P(a,Hc) * F *
P(Hc,a);
155function Ra = local_elim_response(a,
P, rho, lambda, mu, cs2, svcMAP, ...
156 lambda0, arvMAP, qsplit, corrections)
157% Near-immediate feedback elimination at station a (Whitt-You Algorithm 2 /
158% Corollary 4.2). Reduced network: collapse only
the equal/lower-rho cloud Hc
159% to instantaneous switches (censoring, eq. 3.8), retaining
the strictly
160% higher-rho stations as real queues so that
the global traffic rates and
the
161% arrival variability from
the upstream bottleneck(s) are preserved. The
162% near-immediate self-
return at a (self-loop phat in
the censored network)
is
163% removed by immediate-feedback elimination (Section 4.1): remove
the self-loop,
164% renormalize a
's remaining routing by 1/(1-phat) and replace a's service by
the
165% geometric-sum service. The IDC equations are re-solved on
the reduced network,
166% then RQ
is applied at a with
the per-visit adjustment W = (1-phat) Wtilde.
167Hc = setdiff(find(rho <= rho(a) + 1e-9), a); % equal/lower-rho cloud (switches)
168Hi = setdiff(find(rho > rho(a) + 1e-9), a); % strictly higher-rho stations
169R = [a, reshape(Hi,1,[])]; % retained stations, a first
172 Fhc = []; G = zeros(0,m); Pred =
P(R,R);
174 Fhc = inv(eye(numel(Hc)) -
P(Hc,Hc)); % fundamental matrix of
the cloud
175 G = Fhc *
P(Hc,R); % first-passage Hc-station -> R
176 Pred =
P(R,R) +
P(R,Hc) * Fhc *
P(Hc,R); % censored routing among R
178phat = Pred(1,1); % near-immediate
return prob at a
179phat = min(max(phat,0), 1 - 1e-9);
181% immediate-feedback elimination at a (position 1 in R)
183 Pred(1,:) = Pred(1,:) / (1 - phat);
187% first-passage external arrival rate and IDC into each retained station
188arvIdc = @(t) map_count_idc(arvMAP, t);
189c2fun_i = @(i,t) qsplit(i)*arvIdc(t) + (1 - qsplit(i));
192 lam0R(rr) = lambda0(R(rr));
194 lam0R(rr) = lam0R(rr) + lambda0(Hc(ii)) * G(ii,rr);
199 if lam0R(rr) <= 0,
continue; end
200 s = lambda0(R(rr)) * (qsplit(R(rr))*map_idc(arvMAP) + (1 - qsplit(R(rr))));
203 ci = qsplit(Hc(ii))*map_idc(arvMAP) + (1 - qsplit(Hc(ii)));
204 s = s + lambda0(Hc(ii)) * g * (g*ci + (1-g));
206 c2a0R(rr) = s / lam0R(rr);
208a0IdcR = @(t) local_fp_ext_idc_R(t, R, Hc, G, lambda0, c2fun_i, lam0R);
210% service data on R; station a gets
the geometric-sum (folded) service
211muR = mu(R); cs2R = cs2(R);
213for rr = 1:m, svcR{rr} = svcMAP{R(rr)}; end
214svcR{1} = local_geom_map(svcMAP{a}, phat);
215muR(1) = (1-phat) * mu(a);
216cs2R(1) = phat + (1-phat) * cs2(a);
217sIdcR = @(t) local_svc_idc(svcR, t);
219% re-solve
the IDC equations on
the reduced network and apply RQ at a
220ctxR = npfqn_traffic_idc(lam0R, Pred, c2a0R, a0IdcR, muR, cs2R, sIdcR, corrections);
221IaFunA = @(x) local_component(ctxR, x, 1);
222[~, Wt] = qsys_gig1_rq(rho(a), muR(1), cs2R(1), IaFunA);
223Ra = (1-phat) * Wt + 1/mu(a); % per-visit adjustment (mean
visits 1/(1-phat))
226function Ir = local_fp_ext_idc_R(t, R, Hc, G, lambda0, c2fun_i, lam0R)
227% First-passage external arrival IDC into each retained station of
the reduced
228% network (superposition of
the direct external and
the cloud-entering splits).
232 if lam0R(rr) <= 0,
continue; end
233 num = lambda0(R(rr)) * c2fun_i(R(rr), t);
236 num = num + lambda0(Hc(ii)) * g * (g*c2fun_i(Hc(ii),t) + (1-g));
238 Ir(rr) = num / lam0R(rr);
242function out = local_geom_map(
map, p)
243% Geometric random sum of i.i.d. PH service times with success prob (1-p):
244% PH(alpha, T) -> PH(alpha, T + p*t0*alpha), t0 = -T*e. Yields
the head-of-line
245% immediate-feedback-eliminated service (Whitt-You Section 4.1).
247e = ones(size(D0,1),1);
250D0m = D0 + p * (t0 * al);
251D1m = (1-p) * (t0 * al);
255function Is = local_svc_idc(svcMAP, t)
256% t
is either a scalar time (same
for all queues) or a length-nq vector giving
257%
the per-queue evaluation time (service IDC evaluated at rho_a * t).
261 tt = repmat(t, nq, 1);
266 Is(a) = map_count_idc(svcMAP{a}, tt(a));
270function ia = local_component(ctx, x, a)