LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
solver_qna.m
1function [Q,U,R,T,C,X,lG,totiter] = solver_qna(sn, options)
2% [Q,U,R,T,C,X,lG,totiter] = SOLVER_QNA(QN, OPTIONS)
3%
4% Copyright (c) 2012-2026, Imperial College London
5% All rights reserved.
6%
7% Implementation as per Section 7.2.3 of N. Gautaum, Analysis of Queues, CRC Press, 2012.
8% Minor corrections applied in discussion with the author.
9%
10% Decomposition-aggregation structure: each sweep superposes the flows into
11% every station (da_traffic_superpos aggregation), solves the stations in
12% isolation, and splits the departure flows; the sweeps are driven to a
13% fixed point on the queue lengths by da_fpi.
14
15config = options.config;
16config.space_max = 1;
17
18K = sn.nclasses;
19rt = sn.rt;
20S = 1./sn.rates;
21scv = sn.scv; scv(isnan(scv))=0;
22
23%% immediate feedback elimination
24% this is adapted for class-switching, an alternative implementation would
25% rescale by sum(rt((i-1)*K+r,(i-1)*K+1:K)) rather than rt((i-1)*K+r,(i-1)*K+r)
26% for i=1:size(rt,1)
27% for r=1:K
28% for j=1:size(rt,2)
29% for s=1:K
30% if i~=j
31% rt((i-1)*K+r, (j-1)*K+s) = rt((i-1)*K+r, (j-1)*K+s) / (1-rt((i-1)*K+r,(i-1)*K+r));
32% end
33% end
34% end
35% S(i,r) = S(i,r) / (1-rt((i-1)*K+r,(i-1)*K+r));
36% scv(i,r) = rt((i-1)*K+r,(i-1)*K+r) + (1-rt((i-1)*K+r,(i-1)*K+r))*scv(i,r);
37% rt((i-1)*K+r,(i-1)*K+r) = 0;
38% end
39% end
40
41%% generate local state spaces
42I = sn.nnodes;
43M = sn.nstations;
44C = sn.nchains;
45V = cellsum(sn.visits);
46Q = zeros(M,K);
47
48U = zeros(M,K);
49R = zeros(M,K);
50T = zeros(M,K);
51X = zeros(1,K);
52
53lambda = zeros(1,C);
54
55if any(isfinite(sn.njobs))
56 % line_error(mfilename,'QNA does not support closed classes.');
57end
58
59%isMixed = isOpen & isClosed;
60%if isMixed
61% treat open as having higher priority than closed
62%sn.classprio(~isfinite(sn.njobs)) = 1 + max(sn.classprio(isfinite(sn.njobs)));
63%end
64
65%% compute departure process at source
66a1 = zeros(M,K);
67a2 = zeros(M,K);
68d2 = zeros(M,1);
69f2 = zeros(M*K,M*K); % scv of each flow pair (i,r) -> (j,s)
70mubar = [];
71c2 = [];
72Wiq = [];
73for ist=1:M
74 for jst=1:M
75 if sn.nodetype(sn.stationToNode(jst)) ~= NodeType.Source
76 for r=1:K
77 for s=1:K
78 if rt((ist-1)*K+r, (jst-1)*K+s)>0
79 f2((ist-1)*K+r, (jst-1)*K+s) = 1; % C^2ij,r
80 end
81 end
82 end
83 end
84 end
85end
86lambdas_inchain = cell(1,C);
87scvs_inchain = cell(1,C);
88d2c = [];
89
90for c=1:C
91 inchain = sn.inchain{c};
92 refStatIdx = sn.refstat(inchain(1));
93 if isfinite(sn.njobs(c))
94 Q(refStatIdx,:)=State.toMarginal(sn,sn.stationToNode(refStatIdx));
95 end
96 lambdas_inchain{c} = sn.rates(refStatIdx,inchain);
97 scvs_inchain{c} = scv(refStatIdx,inchain);
98 lambda(c) = sum(lambdas_inchain{c}(isfinite(lambdas_inchain{c})));
99 d2c(c) = da_traffic_superpos(lambdas_inchain{c},scvs_inchain{c});
100 if isinf(sum(sn.njobs(inchain))) % if open chain
101 T(refStatIdx,inchain') = lambdas_inchain{c};
102 end
103end
104
105for c=1:C
106 inchain = sn.inchain{c};
107 refStatIdx = sn.refstat(inchain(1));
108 d2(refStatIdx)=d2c*lambda'/sum(lambda);
109end
110
111%% main iteration
112fpopts = options;
113fpopts.iter_max = options.iter_max + 1; % legacy while-loop executed one extra sweep at the cap
114fpopts.config.da_nanstop = true; % legacy while-loop exited on NaN convergence measure
115[Q, totiter] = da_fpi(@qna_sweep, Q, fpopts);
116
117 function [Qnew, Qref] = qna_sweep(Qin, itnum)
118 Q = Qin;
119 for c=1:C
120 inchain = sn.inchain{c};
121 Q(:,c) = sn.njobs(c) .* Q(:,c) / sum(Q(:,c));
122 end
123 Qref = Q;
124
125 for k=1:K
126 if sn.isslc(k)
127 Q(:,k) = 0;
128 Q(sn.refstat(k),k) = sn.njobs(k);
129 end
130 end
131
132 % update throughputs at all stations
133 if itnum==1
134 for c=1:C
135 inchain = sn.inchain{c};
136 for m=1:M
137 T(m,inchain) = V(m,inchain) .* lambda(c);
138 end
139 end
140 end
141
142 % superposition
143 for ist=1:M
144 a1(ist,:) = 0;
145 a2(ist,:) = 0;
146 lambda_i = sum(T(ist,:));
147 for jst=1:M
148 for r=1:K
149 for s=1:K
150 a1(ist,r) = a1(ist,r) + T(jst,s)*rt((jst-1)*K+s, (ist-1)*K+r);
151 a2(ist,r) = a2(ist,r) + (1/lambda_i) * f2((jst-1)*K+s, (ist-1)*K+r)*T(jst,s)*rt((jst-1)*K+s, (ist-1)*K+r);
152 end
153 end
154 end
155 end
156
157 % update flow trhough queueing station
158 for ind=1:I
159 if sn.isstation(ind)
160 ist = sn.nodeToStation(ind);
161 switch sn.nodetype(ind)
162 case NodeType.Join
163 % no-op
164 % for c=1:C
165 % inchain = sn.inchain{c};
166 % for k=inchain
167 % fanin = nnz(sn.rtnodes(:, (ind-1)*K+k));
168 % TN(ist,k) = lambda(c)*V(ist,k)/fanin;
169 % UN(ist,k) = 0;
170 % QN(ist,k) = 0;
171 % RN(ist,k) = 0;
172 % end
173 % end
174 otherwise
175 switch sn.sched(ist)
176 case SchedStrategy.INF
177 for r=1:K
178 for s=1:K
179 d2(ist,s) = a2(ist,s);
180 end
181 end
182 for c=1:C
183 inchain = sn.inchain{c};
184 for k=inchain
185 T(ist,k) = a1(ist,k);
186 Q(ist,k) = T(ist,k).*S(ist,k)*V(ist,k);
187 U(ist,k) = Q(ist,k);
188 R(ist,k) = Q(ist,k)/T(ist,k);
189 end
190 end
191 case SchedStrategy.PS
192 for c=1:C
193 inchain = sn.inchain{c};
194 for k=inchain
195 T(ist,k) = lambda(c)*V(ist,k);
196 U(ist,k) = S(ist,k)*T(ist,k);
197 end
198 Nc = sum(sn.njobs(inchain)); % closed population
199 Uden = min([1-options.tol,sum(U(ist,:))]);
200 for k=inchain
201 Q(ist,k) = (U(ist,k)-U(ist,k)^(Nc+1))/(1-Uden); % geometric bound type approximation
202 %Q(ist,k) = UN(ist,k)/(1-Uden);
203 R(ist,k) = Q(ist,k)/T(ist,k);
204 end
205 end
206 case {SchedStrategy.FCFS}
207 mu_ist = sn.rates(ist,1:K);
208 mu_ist(isnan(mu_ist))=0;
209 rho_ist_class = a1(ist,1:K)./(GlobalConstants.FineTol+sn.rates(ist,1:K));
210 rho_ist_class(isnan(rho_ist_class))=0;
211 lambda_ist = sum(a1(ist,:));
212 mi = sn.nservers(ist);
213 rho_ist = sum(rho_ist_class) / mi;
214 if rho_ist < 1-options.tol
215 for k=1:K
216 if rho_ist > 0.7
217 alpha_mi = (rho_ist^mi+rho_ist) / 2;
218 else
219 alpha_mi = rho_ist^((mi+1)/2);
220 end
221 mubar(ist) = lambda_ist ./ rho_ist;
222 c2(ist) = -1;
223 for r=1:K
224 if mu_ist(r)>0
225 c2(ist) = c2(ist) + a1(ist,r)/lambda_ist * (mubar(ist)/mi/mu_ist(r))^2 * (scv(ist,r)+1 );
226 end
227 end
228 Wiq(ist) = (alpha_mi / mubar(ist)) * 1/ (1-rho_ist) * (sum(a2(ist,:))+c2(ist))/2;
229 Q(ist,k) = a1(ist,k) / mu_ist(k) + a1(ist,k)*Wiq(ist);
230 end
231 d2(ist) = 1 + rho_ist^2*(c2(ist)-1)/sqrt(mi) + (1 - rho_ist^2) *(sum(a2(ist,:))-1);
232 else
233 for k=1:K
234 Q(ist,k) = sn.njobs(k);
235 end
236 d2(ist) = 1;
237 end
238 for k=1:K
239 T(ist,k) = a1(ist,k);
240 U(ist,k) = T(ist,k) * S(ist,k) /sn.nservers(ist);
241 R(ist,k) = Q(ist,k) ./ T(ist,k);
242 end
243 end
244 end
245 else % not a station
246 switch sn.nodetype(ind)
247 case NodeType.Fork
248 % Fork splits traffic; routing and SCV splitting
249 % are handled by the rt matrix and the splitting
250 % formula in the main iteration loop. No additional
251 % processing needed here.
252 end
253 end
254 end
255
256
257 % splitting - update flow scvs
258 for ist=1:M
259 for jst=1:M
260 if sn.nodetype(sn.stationToNode(jst)) ~= NodeType.Source
261 for r=1:K
262 for s=1:K
263 if rt((ist-1)*K+r, (jst-1)*K+s)>0
264 f2((ist-1)*K+r, (jst-1)*K+s) = 1 + rt((ist-1)*K+r, (jst-1)*K+s) * (d2(ist)-1); % C^2ij,r
265 end
266 end
267 end
268 end
269 end
270 end
271
272 Qnew = Q;
273 end
274
275for k=1:K
276 if sn.isslc(k)
277 Q(:,k) = 0;
278 ist = sn.refstat(k);
279 Q(ist,k) = sn.njobs(k);
280 T(ist,k) = sn.njobs(k)*sn.rates(ist,k);
281 R(ist,k) = Q(ist,k) ./ T(ist,k);
282 U(ist,k) = S(ist,k)*T(ist,k);
283 end
284end
285
286
287for c=1:C
288 inchain = sn.inchain{c};
289 if isfinite(sn.njobs(c))
290 Q(:,c) = sn.njobs(c) .* Q(:,c) / sum(Q(:,c));
291 end
292end
293for ist=1:sn.nstations
294 switch sn.sched(ist)
295 case SchedStrategy.INF
296 U(ist,:) = Q(ist,:);
297 end
298end
299
300C = sum(R,1);
301Q = abs(Q);
302Q(isnan(Q))=0;
303U(isnan(U))=0;
304R(isnan(R))=0;
305C(isnan(C))=0;
306X(isnan(X))=0;
307lG = 0;
308end
Definition Station.m:245