1function ctx = npfqn_traffic_idc(lambda0,
P, c2a0, a0IdcFun, mu, cs2, sIdcFun, corrections)
2% ctx = NPFQN_TRAFFIC_IDC(lambda0,
P,c2a0,a0IdcFun,mu,cs2,sIdcFun)
4% Traffic variability equations
for the Robust Queueing Network Analyzer
5% (RQNA) of W. Whitt and W. You (2018),
"A Robust Queueing Network Analyzer
6% Based on Indices of Dispersion". Assembles and solves:
7% -
the limiting variability equations (eq. 42/44) for
the asymptotic
8% total-arrival variability parameters c2_{a,i} = I_{a,i}(Inf);
9% - a solver (handle ctx.IaFun)
for the time-dependent index of dispersion
10%
for counts (IDC) equations (eq. 40/43), returning I_{a,i}(t)
for all
11% internal arrival flows,
using the default correction terms alpha_{i,j}
12% (eq. 34) and beta_i (eqs. 38-39) and tuning function h(rho)=rho^2.
14% This models a single-
class open queueing network of K single-server FCFS
15% queues with Markovian routing
P (
P(i,j)=p_{i,j}).
17% Inputs (all K-vectors are
column vectors, K = number of queues):
18% lambda0 : external arrival rate into each queue
19%
P : KxK routing matrix among queues
20% c2a0 : asymptotic IDC (SCV) of each external arrival process
21% a0IdcFun : handle, a0IdcFun(t) -> Kx1 external arrival IDC I_{a,0,i}(t)
22% mu : service rate at each queue
23% cs2 : service SCV c2_{s,i}
24% sIdcFun : handle, sIdcFun(t) -> Kx1 service IDC I_{s,i}(t)
26% Output ctx: struct with fields
27% lambda,rho,Xi,c2a,c2d,c2aij,c2x and function handle IaFun(t) returning
the
28% Kx1 vector of total arrival IDCs I_{a,i}(t).
31lambda0 = lambda0(:); mu = mu(:); cs2 = cs2(:); c2a0 = c2a0(:);
32if nargin < 8 || isempty(corrections)
33 corrections =
struct();
35if ~isfield(corrections,
'alpha'), corrections.alpha =
true; end
36if ~isfield(corrections,
'beta'), corrections.beta =
true; end
37useAlpha = corrections.alpha; useBeta = corrections.beta;
39% ----- traffic rate equations (eq. 20-21) -----
40Xi = inv(eye(K) -
P'); % fundamental matrix (I-P')^{-1}
41lambda = Xi * lambda0; % total arrival rate at each queue
43lam_ji = (lambda(:) * ones(1,K)) .*
P; % lam_ji(j,i) = lambda_j p_{j,i}
45% ----- correction terms (asymptotic, w*(Inf)=1) -----
46% alpha: c2alpha_{i,j} = 2 Xi_{i,j} p_{i,j} (1-p_{i,j})
47c2alpha = 2 * Xi .*
P .* (1 -
P); % KxK
48if ~useAlpha, c2alpha = zeros(K,K); end
50% beta: zeta_{j,i;k,i} from eq. (39), then c2beta_i = (2/lambda_i) sum_{j<k} zeta
51Sigma = cell(K,1); % splitting covariance at each station l
53 pl =
P(l,:); % 1xK routing out of l
54 Sl = -(pl
' * pl) * lambda(l);
55 Sl(1:K+1:end) = pl .* (1 - pl) * lambda(l);
58Amat = diag(c2a0 .* lambda0); % external arrival Brownian variance-rate
60 Amat = Amat + Sigma{l};
62% zetaAll{i} is KxK with entry (j,k) = zeta_{j,i;k,i}
66 nu = (P(:,i) * ones(1,K)) .* Xi; % nu(l,:) = p_{l,i} * Xi(l,:) (row l)
67 Z = nu * Amat * nu'; % first term
for all (j,k)
68 % add cross terms nu_k*Sigma_j*e_i + nu_j*Sigma_k*e_i
71 Z(j,k) = Z(j,k) + nu(k,:)*Sigma{j}(:,i) + nu(j,:)*Sigma{k}(:,i);
82 c2beta(i) = (2/lambda(i)) * s;
85if ~useBeta, c2beta = zeros(K,1); zetaAll = repmat({zeros(K,K)},K,1); end
87% ----- limiting variability equations (eq. 44): (E - Minf) c = binf -----
88% variable ordering: [c2a(1..K), c2aij(i,j) row-major, c2d(1..K)]
89Na = K; Naij = K*K; Nd = K;
92iaij= @(i,j) Na + (i-1)*K + j;
93id = @(i) Na + Naij + i;
98 % c2a_i = sum_j (lam_ji/lambda_i) c2aij_{j,i} + (lambda0_i/lambda_i) c2a0_i + c2beta_i
101 Minf(ia(i), iaij(j,i)) = lam_ji(j,i) / lambda(i);
103 binf(ia(i)) = (lambda0(i)/lambda(i)) * c2a0(i) + c2beta(i);
106 % c2aij_{i,j} = p_{i,j} c2d_i + (1-p_{i,j}) + c2alpha_{i,j}
107 Minf(iaij(i,j),
id(i)) =
P(i,j);
108 binf(iaij(i,j)) = (1 -
P(i,j)) + c2alpha(i,j);
111 Minf(
id(i), ia(i)) = 1;
113csol = (eye(N) - Minf) \ binf;
115c2aij= reshape(csol(Na+1:Na+Naij), K, K)
'; % c2aij(i,j)
116c2d = csol(Na+Naij+1:end);
117c2x = c2a + cs2; % c2_{x,i} = I_{a,i}(Inf)+c2_{s,i}
119% ----- assemble context and time-dependent solver -----
121ctx.K = K; ctx.lambda = lambda; ctx.lambda0 = lambda0; ctx.mu = mu;
122ctx.rho = rho; ctx.Xi = Xi; ctx.P = P; ctx.cs2 = cs2;
123ctx.c2a = c2a; ctx.c2aij = c2aij; ctx.c2d = c2d; ctx.c2x = c2x;
124ctx.c2a0 = c2a0; ctx.lam_ji = lam_ji;
125ctx.zetaAll = zetaAll; ctx.c2alpha = c2alpha;
126ctx.a0IdcFun = a0IdcFun; ctx.sIdcFun = sIdcFun;
127ctx.IaFun = @(t) local_idc_at(ctx, t);
130function Ia = local_idc_at(ctx, t)
131% Solve the time-dependent IDC equations (43) at a single time t; return the
132% Kx1 vector of total arrival IDCs I_{a,i}(t).
133K = ctx.K; P = ctx.P; lambda = ctx.lambda; rho = ctx.rho;
134c2x = ctx.c2x; lam_ji = ctx.lam_ji; Xi = ctx.Xi;
135h = rho.^2; % tuning function h(rho)=rho^2
137% departure weights w_i(t) = w*((1-rho_i)^2 lambda_i t /(h_i c2x_i))
140 if h(i) > 0 && c2x(i) > 0
141 warg(i) = (1-rho(i))^2 * lambda(i) * t / (h(i) * c2x(i));
146w = npfqn_rqna_weight(warg);
148Ia0 = ctx.a0IdcFun(t); Ia0 = Ia0(:);
149Is = ctx.sIdcFun(rho .* t); Is = Is(:); % service IDC at scaled time rho*t
151% time-dependent correction terms
152% alpha_{i,j}(t) = c2alpha_{i,j} * w_i(t)
153alpha_t = ctx.c2alpha .* (w * ones(1,K)); % row i scaled by w(i)
154% beta_i(t) = (1/lambda_i) sum_{j~=k} zeta_{j,i;k,i} w*(arg_j), where the weight
155% for source station j feeding i uses arg_j = (1-rho_j)^2 p_{j,i} lambda_j t/(h_j c2x_j)
159 % weight for source station j feeding i:
162 if h(j) > 0 && c2x(j) > 0 && P(j,i) > 0
163 aj = (1-rho(j))^2 * P(j,i) * lambda(j) * t / (h(j) * c2x(j));
164 wj(j) = npfqn_rqna_weight(aj);
171 s = s + Z(j,k) * wj(j);
176 beta_t(i) = s / lambda(i);
180% assemble (E - M(t)) I = b(t)
181Na = K; Naij = K*K; Nd = K; N = Na+Naij+Nd;
182ia = @(i) i; iaij= @(i,j) Na+(i-1)*K+j; idd = @(i) Na+Naij+i;
183M = zeros(N,N); b = zeros(N,1);
187 M(ia(i), iaij(j,i)) = lam_ji(j,i)/lambda(i);
189 b(ia(i)) = (ctx.lambda0(i)/lambda(i))*Ia0(i) + beta_t(i);
192 M(iaij(i,j), idd(i)) = P(i,j);
193 b(iaij(i,j)) = (1 - P(i,j)) + alpha_t(i,j);
195 M(idd(i), ia(i)) = w(i);
196 b(idd(i)) = (1 - w(i)) * Is(i);
198sol = (eye(N) - M) \ b;