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