LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
solver_amvald_forward.m
1function [Wchain, STeff] = solver_amvald_forward(M, K, nservers, schedparam, lldscaling, cdscaling, ljdscaling, ljdcutoffs, ljcdscaling, ljcdcutoffs, sched, classprio, gamma, tau, Qchain_in, Xchain_in, Uchain_in, STchain_in, Vchain_in, Nchain_in, SCVchain_in, options)
2
3%Uhiprio = zeros(M,K); % utilization due to "permanent jobs" in DPS
4
5if isempty(gamma)
6 gamma = zeros(M,K);
7end
8
9Nt = sum(Nchain_in(isfinite(Nchain_in)));
10if all(isinf(Nchain_in))
11 delta = 1;
12else
13 delta = (Nt - 1) / Nt;
14end
15
16deltaclass = (Nchain_in - 1) ./ Nchain_in;
17deltaclass(isinf(Nchain_in)) = 1;
18
19ocl = find(isinf(Nchain_in));
20ccl = find(isfinite(Nchain_in) & Nchain_in>0);
21nnzclasses = find(Nchain_in>0);
22nnzclasses_eprio = cell(1,length(nnzclasses));
23nnzclasses_hprio = cell(1,length(nnzclasses));
24nnzclasses_ehprio = cell(1,length(nnzclasses));
25if max(classprio) ~= min(classprio)
26 for r = nnzclasses
27 nnzclasses_eprio{r} = intersect(nnzclasses, find(classprio == classprio(r))); % equal prio
28 nnzclasses_hprio{r} = intersect(nnzclasses, find(classprio < classprio(r))); % higher prio (lower value = higher priority)
29 nnzclasses_ehprio{r} = intersect(nnzclasses, find(classprio <= classprio(r))); % equal or higher prio
30 end
31else
32 for r = nnzclasses
33 nnzclasses_eprio{r} = nnzclasses;
34 nnzclasses_hprio{r} = [];
35 nnzclasses_ehprio{r} = [];
36 end
37end
38
39%% evaluate lld and cd correction factors
40totArvlQlenSeenByOpen = zeros(K,M,1);
41interpTotArvlQlen = zeros(M,1);
42totArvlQlenSeenByClosed = zeros(M,K);
43stationaryQlen = zeros(M,K);
44selfArvlQlenSeenByClosed = zeros(M,K);
45for k=1:M
46 interpTotArvlQlen(k) = delta * sum(Qchain_in(k,nnzclasses));
47 for r = nnzclasses
48 selfArvlQlenSeenByClosed(k,r) = deltaclass(r) * Qchain_in(k,r); % qlen of same class as arriving one
49 switch sched(k)
50 case {SchedStrategy.HOL}
51 totArvlQlenSeenByOpen(r,k) = sum(Qchain_in(k,nnzclasses_ehprio{r}));
52 totArvlQlenSeenByClosed(k,r) = deltaclass(r) * Qchain_in(k,r) + sum(Qchain_in(k,setdiff(nnzclasses_ehprio{r},r)));
53 otherwise
54 totArvlQlenSeenByOpen(r,k) = sum(Qchain_in(k,nnzclasses));
55 totArvlQlenSeenByClosed(k,r) = deltaclass(r) * Qchain_in(k,r) + sum(Qchain_in(k,nnzclasses)) - Qchain_in(k,r);
56 end
57 stationaryQlen(k,r) = Qchain_in(k,r); % qlen of same class as arriving one
58 end
59end
60
61%% all methods use limited-load dependent (LLD) and class-dependent (CD) corrections from QD-AMVA
62if isempty(lldscaling)
63 lldscaling = ones(M,ceil(Nt));
64end
65
66switch options.method
67 case {'lin', 'qdlin'}
68 if isempty(nnzclasses)
69 lldterm = pfqn_lldfun(1 + interpTotArvlQlen, lldscaling);
70 else
71 lldterm = ones(M,K);
72 for r=nnzclasses
73 if ~isempty(ccl)
74 lldterm(:,r) = pfqn_lldfun(1 + interpTotArvlQlen + Nchain_in(ccl)*permute(gamma(r,k,ccl),3:-1:1) - gamma(r,k,r), lldscaling);
75 end
76 end
77 end
78 otherwise
79 lldterm = pfqn_lldfun(1 + interpTotArvlQlen, lldscaling);
80end
81
82cdterm = ones(M,K);
83switch options.method
84 case {'lin', 'qdlin'}
85 for r=nnzclasses
86 if ~isempty(cdscaling)
87 if isfinite(Nchain_in(r))
88 cdterm(:,r) = pfqn_cdfun(1 + selfArvlQlenSeenByClosed + (Nchain_in(r) - 1)*gamma(r,k,r), cdscaling); % qd-amva class-dependence term
89 else
90 cdterm(:,r) = pfqn_cdfun(1 + stationaryQlen + (Nchain_in(r) - 1)*gamma(r,k,r), cdscaling); % qd-amva class-dependence term
91 end
92 end
93 end
94 otherwise
95 for r=nnzclasses
96 if ~isempty(cdscaling)
97 if isfinite(Nchain_in(r))
98 cdterm(:,r) = pfqn_cdfun(1 + selfArvlQlenSeenByClosed, cdscaling); % qd-amva class-dependence term
99 else
100 cdterm(:,r) = pfqn_cdfun(1 + stationaryQlen, cdscaling); % qd-amva class-dependence term
101 end
102 end
103 end
104end
105
106%% Joint dependence correction terms
107% Both LJD and LJCD are indexed by population vector n = (n1, n2, ..., nK)
108% - LJD: scaling = table[idx(n)] - same scaling applied to all classes
109% - LJCD: scaling_r = table_r[idx(n)] - each class r has its own table
110% If LJCD is set for a station, it overrides LJD for that station.
111%
112% For FES (Flow-Equivalent Server), the scaling tables contain throughputs X(n).
113% Since these are rates, we divide to get time scaling: ljdterm = 1/X(n).
114% For multi-class, we interpolate using neighboring population states.
115
116ljdterm = ones(M, K); % Combined term: (M x K) matrix
117hasLjcd = ~isempty(ljcdscaling) && any(~cellfun(@isempty, ljcdscaling));
118hasLjd = ~isempty(ljdscaling) && any(~cellfun(@isempty, ljdscaling));
119
120
121for k = 1:M
122 % Get population vector from queue lengths at station k
123 nvec = Qchain_in(k, :);
124
125 if hasLjcd && ~isempty(ljcdscaling{k})
126 % LJCD: per-class scaling
127 % The scaling table contains throughputs X(n) indexed by population n.
128 % For FES, the "population" is the number of jobs at the station.
129 % Use ceiling of queue length + 1 to account for the arriving job.
130 cutoffs = ljcdcutoffs{k};
131
132 % For FES, use ceiling of queue length as the population index
133 % The +1 helps for single-class but not multi-class
134 if K == 1
135 nIndex = ceil(nvec + 1);
136 nIndex = max(1, min(nIndex, cutoffs));
137 else
138 nIndex = ceil(nvec);
139 nIndex = max(0, min(nIndex, cutoffs));
140 end
141 idx = ljd_linearize(nIndex, cutoffs);
142
143 for r = 1:K
144 if r <= length(ljcdscaling{k}) && ~isempty(ljcdscaling{k}{r})
145 table = ljcdscaling{k}{r};
146
147 % Lookup throughput at population index
148 if idx <= length(table)
149 Xval = table(idx);
150 else
151 Xval = 0;
152 end
153
154 % Convert rate to time scaling (divide by throughput)
155 if Xval > GlobalConstants.FineTol
156 ljdterm(k, r) = 1.0 / Xval;
157 else
158 ljdterm(k, r) = 1.0; % No scaling when throughput is zero
159 end
160
161 end
162 end
163 elseif hasLjd && ~isempty(ljdscaling{k})
164 % LJD: single scaling applied to all classes
165 % Lookup: idx = linearize(n1, ..., nK), then table[idx]
166 nClamped = max(0, min(ceil(nvec), ljdcutoffs(k, :)));
167 idx = ljd_linearize(nClamped, ljdcutoffs(k, :));
168 if idx <= length(ljdscaling{k})
169 ljdterm(k, :) = ljdscaling{k}(idx); % Same value for all classes
170 end
171 end
172end
173
174switch options.config.multiserver
175 case 'softmin' % softmin on every station, may not always converge
176 switch options.method
177 case {'lin', 'qdlin'}
178 g = 0;
179 for r=ccl
180 g = g + ((Nt-1)/Nt) * Nchain_in(r) * gamma(ccl,:,r);
181 end
182 msterm = pfqn_lldfun(1 + interpTotArvlQlen + mean(g, 1)', [], nservers); % if native qd then account for multiserver in the correction terms
183 otherwise
184 msterm = pfqn_lldfun(1 + interpTotArvlQlen + (Nt-1)*mean(gamma(ccl,:), 1)', [], nservers); % if native qd then account for multiserver in the correciton terms
185 end
186 case 'seidmann'
187 msterm = ones(M,1) ./ nservers(:);
188 msterm(msterm==0) = 1; % infinite server case
189 case 'default' % softmin on ps stations, seidmann on fcfs stations
190 switch options.method
191 case {'lin', 'qdlin'}
192 g = 0;
193 for r=ccl
194 g = g + ((Nt-1)/Nt) * Nchain_in(r) * gamma(ccl,:,r);
195 end
196 msterm = pfqn_lldfun(1 + interpTotArvlQlen + mean(g,1)', [], nservers); % if native qd then account for multiserver in the correction terms
197 otherwise
198 g = 0;
199 for r=ccl
200 g = g + (Nt-1)*gamma(r,:);
201 end
202 msterm = pfqn_lldfun(1 + interpTotArvlQlen + mean(g), [], nservers); % if native qd then account for multiserver in the correction terms
203 end
204 fcfstypeset = find(sched==SchedStrategy.FCFS | sched==SchedStrategy.SIRO | sched==SchedStrategy.LCFSPR);
205 msterm(fcfstypeset) = 1 ./ nservers(fcfstypeset);
206 case 'suri'
207 msterm = ones(M,1); % No demand scaling; multiserver handled via suriFactor in Wchain
208 otherwise
209 line_error(mfilename,'Unrecognized multiserver approximation method');
210end
211
212% Compute Suri correction factor per station
213suriFactor = ones(M,1);
214if strcmp(options.config.multiserver, 'suri')
215 suri_alpha = 4.464;
216 suri_beta = 0.676;
217 for k=1:M
218 c = nservers(k);
219 if c > 1 && isfinite(c)
220 rho_k = min(sum(Uchain_in(k,:), 'omitnan') / c, 1 - options.tol);
221 if rho_k > 0
222 suriFactor(k) = rho_k^(suri_alpha * (c^suri_beta - 1)) / c;
223 else
224 suriFactor(k) = 1 / c;
225 end
226 elseif isinf(c) || c == 0
227 suriFactor(k) = 0;
228 end
229 end
230end
231
232Wchain = zeros(M,K);
233
234STeff = zeros(size(STchain_in)); % effective service time
235lldterm = repmat(lldterm, 1, K);
236% ljdterm is already (M, K) - contains either LJD or LJCD values
237for r=nnzclasses
238 for k=1:M
239 STeff(k,r) = STchain_in(k,r) * lldterm(k,r) * msterm(k) * cdterm(k,r) * ljdterm(k,r);
240 end
241end
242
243%% if amva.qli or amva.fli, update now totArvlQlenSeenByClosed with STeff
244switch options.method
245 case 'qli' % Wang-Sevcik queue line
246 infset = sched == SchedStrategy.INF;
247 for k=1:M
248 switch sched(k)
249 case {SchedStrategy.HOL}
250 for r = nnzclasses
251 if Nchain_in(r) == 1
252 totArvlQlenSeenByClosed(k,r) = sum(Qchain_in(k,nnzclasses_ehprio{r})) - Qchain_in(k,r);
253 else
254 qlinum = STeff(k,r) * (1+sum(Qchain_in(k,nnzclasses_ehprio{r})) - Qchain_in(k,r));
255 qliden = sum(STeff(infset,r));
256 for m=1:M
257 qliden = qliden + STeff(m,r) * (1+sum(Qchain_in(m,nnzclasses_ehprio{r})) - Qchain_in(m,r));
258 end
259 totArvlQlenSeenByClosed(k,r) = sum(Qchain_in(k,nnzclasses_ehprio{r})) - (1/(Nchain_in(r)-1))*(Qchain_in(k,r) - qlinum/qliden);
260 end
261 end
262 otherwise
263 for r = nnzclasses
264 if Nchain_in(r) == 1
265 totArvlQlenSeenByClosed(k,r) = sum(Qchain_in(k,nnzclasses)) - Qchain_in(k,r);
266 else
267 qlinum = STeff(k,r) * (1+sum(Qchain_in(k,nnzclasses)) - Qchain_in(k,r));
268 qliden = sum(STeff(infset,r));
269 for m=1:M
270 qliden = qliden + STeff(m,r) * (1+sum(Qchain_in(m,nnzclasses)) - Qchain_in(m,r));
271 end
272 totArvlQlenSeenByClosed(k,r) = sum(Qchain_in(k,nnzclasses)) - (1/(Nchain_in(r)-1))*(Qchain_in(k,r) - qlinum/qliden);
273 end
274 end
275 end
276 end
277 case 'fli' % Wang-Sevcik fraction line
278 infset = sched == SchedStrategy.INF;
279 for k=1:M
280 switch sched(k)
281 case {SchedStrategy.HOL}
282 for r = nnzclasses
283 if Nchain_in(r) == 1
284 totArvlQlenSeenByClosed(k,r) = sum(Qchain_in(k,nnzclasses_ehprio{r})) - Qchain_in(k,r);
285 else
286 qlinum = STeff(k,r) * (1+sum(Qchain_in(k,nnzclasses_ehprio{r})) - Qchain_in(k,r));
287 qliden = sum(STeff(infset,r));
288 for m=1:M
289 qliden = qliden + STeff(m,r) * (1+sum(Qchain_in(m,nnzclasses_ehprio{r})) - Qchain_in(m,r));
290 end
291 totArvlQlenSeenByClosed(k,r) = sum(Qchain_in(k,nnzclasses_ehprio{r})) - (2/Nchain_in(r))*Qchain_in(k,r) + qlinum/qliden;
292 end
293 end
294 otherwise
295 for r = nnzclasses
296 if Nchain_in(r) == 1
297 totArvlQlenSeenByClosed(k,r) = sum(Qchain_in(k,nnzclasses)) - Qchain_in(k,r);
298 else
299 qlinum = STeff(k,r) * (1+sum(Qchain_in(k,nnzclasses)) - Qchain_in(k,r));
300 qliden = sum(STeff(infset,r));
301 for m=1:M
302 qliden = qliden + STeff(m,r) * (1+sum(Qchain_in(m,nnzclasses)) - Qchain_in(m,r));
303 end
304 totArvlQlenSeenByClosed(k,r) = sum(Qchain_in(k,nnzclasses)) - (2/Nchain_in(r))*Qchain_in(k,r) + qlinum/qliden;
305 end
306 end
307
308 end
309 end
310end
311
312%% compute response times from current queue-lengths
313for ir=1:length(nnzclasses)
314 r=nnzclasses(ir);
315 sd = nnzclasses;
316 sd(ir)=[];
317 %sdprio = setdiff(nnzclasses_ehprio{r},r);
318
319 for k=1:M
320
321 switch sched(k)
322 case SchedStrategy.INF
323 Wchain(k,r) = STeff(k,r);
324
325 case SchedStrategy.PS
326 switch options.method
327 case {'qd', 'lin', 'qdlin'} % QD-LIN interpolation
328 switch options.config.multiserver
329 case 'seidmann' % in this case, qd handles only lld and cd scalings
330 Wchain(k,r) = STeff(k,r) * (nservers(k)-1); % multi-server correction with serial think time, (1/nservers(k)) term already in STeff
331 if ismember(r,ocl)
332 Wchain(k,r) = Wchain(k,r) + STeff(k,r) * (1 + totArvlQlenSeenByOpen(r,k));
333 else
334 Wchain(k,r) = Wchain(k,r) + STeff(k,r) * (1 + interpTotArvlQlen(k) + Nchain_in(ccl)*permute(gamma(r,k,ccl),3:-1:1) - gamma(r,k,r));
335 end
336 case {'default','softmin'}
337 if ismember(r,ocl)
338 Wchain(k,r) = STeff(k,r) * (1 + totArvlQlenSeenByOpen(r,k));
339 else
340 switch options.method
341 case {'lin', 'qdlin'} % Linearizer
342 Wchain(k,r) = Wchain(k,r) + STeff(k,r) * (1 + interpTotArvlQlen(k) + Nchain_in(ccl)*permute(gamma(r,k,ccl),3:-1:1) - gamma(r,k,r));
343 otherwise
344 Wchain(k,r) = STeff(k,r) * (1 + interpTotArvlQlen(k) + (Nt-1)*gamma(r,k));
345 end
346 end
347 case 'suri'
348 if ismember(r,ocl)
349 Wchain(k,r) = STeff(k,r) * (1 + totArvlQlenSeenByOpen(r,k) * suriFactor(k));
350 else
351 switch options.method
352 case {'lin', 'qdlin'}
353 Wchain(k,r) = STeff(k,r) * (1 + (interpTotArvlQlen(k) + Nchain_in(ccl)*permute(gamma(r,k,ccl),3:-1:1) - gamma(r,k,r)) * suriFactor(k));
354 otherwise
355 Wchain(k,r) = STeff(k,r) * (1 + (interpTotArvlQlen(k) + (Nt-1)*gamma(r,k)) * suriFactor(k));
356 end
357 end
358 end
359 otherwise
360 switch options.config.multiserver
361 case 'seidmann'
362 Wchain(k,r) = STeff(k,r) * (nservers(k)-1); % multi-server correction with serial think time, (1/nservers(k)) term already in STeff
363 if ismember(r,ocl)
364 Wchain(k,r) = Wchain(k,r) + STeff(k,r) * (1 + totArvlQlenSeenByOpen(r,k));
365 else
366 Wchain(k,r) = Wchain(k,r) + STeff(k,r) * (1 + totArvlQlenSeenByClosed(k) + (Nt-1)*gamma(r,k));
367 end
368 case {'default','softmin'}
369 if ismember(r,ocl)
370 Wchain(k,r) = Wchain(k,r) + STeff(k,r) * (1 + totArvlQlenSeenByOpen(r,k));
371 else
372 Wchain(k,r) = Wchain(k,r) + STeff(k,r) * (1 + totArvlQlenSeenByClosed(k) + (Nt-1)*gamma(r,k));
373 end
374 case 'suri'
375 if ismember(r,ocl)
376 Wchain(k,r) = STeff(k,r) * (1 + totArvlQlenSeenByOpen(r,k) * suriFactor(k));
377 else
378 Wchain(k,r) = STeff(k,r) * (1 + (totArvlQlenSeenByClosed(k) + (Nt-1)*gamma(r,k)) * suriFactor(k));
379 end
380 end
381 end
382
383 case SchedStrategy.DPS
384 w = schedparam; % DPS weight
385 tss = Inf; % time-scale separation threshold, this was originally at 5, presently it is disabled
386
387 if nservers(k) > 1
388 % Multi-server DPS: seidmann-style correction
389 % STeff already contains 1/nservers(k) factor from chain aggregation
390 Wchain(k,r) = STeff(k,r) * (nservers(k) - 1); % serial think time correction
391 end
392
393 Wchain(k,r) = Wchain(k,r) + STeff(k,r) * (1 + selfArvlQlenSeenByClosed(k,r)); % class-r
394 for s=sd % slowdown due to classes s!=r
395 if w(k,s) == w(k,r) % handle gracefully 0/0 case
396 Wchain(k,r) = Wchain(k,r) + STeff(k,r) * stationaryQlen(k,s);
397 elseif w(k,s)/w(k,r)<=tss
398 Wchain(k,r) = Wchain(k,r) + STeff(k,r) * stationaryQlen(k,s) * w(k,s)/w(k,r);
399 elseif w(k,s)/w(k,r)>tss
400 % if there is time-scale separation, do nothing
401 % all is accounted for by 1/(1-Uhiprio)
402 end
403 end
404
405 case {SchedStrategy.FCFS, SchedStrategy.SIRO, SchedStrategy.LCFSPR}
406 if STeff(k,r) > 0
407 Uchain_r = Uchain_in ./ repmat(Xchain_in,M,1) .* (repmat(Xchain_in,M,1) + repmat(tau(r,:),M,1));
408
409 if nservers(k)>1
410 deltaclass_r = ones(size(Xchain_in));
411 deltaclass_r(r) = deltaclass(r);
412 if sum(deltaclass_r .* Xchain_in .* Vchain_in(k,:) .* STeff(k,:)) < 0.75 % light-load case
413 Bk = ((deltaclass_r .* Xchain_in .* Vchain_in(k,:) .* STeff(k,:))); % note: this is in 0-1 as a utilization
414 else % high-load case
415 Bk = (deltaclass_r .* Xchain_in .* Vchain_in(k,:) .* STeff(k,:)).^(nservers(k)-1);
416 end
417 else
418 Bk = ones(1,K);
419 end
420
421 hasLjd = ~isempty(ljdscaling) && any(~cellfun(@isempty, ljdscaling));
422 if nservers(k)==1 && (~isempty(lldscaling) || ~isempty(cdscaling) || hasLjd)
423 switch options.config.highvar % high SCV
424 case 'hvmva'
425 Wchain(k,r) = STeff(k,r) * (1-sum(Uchain_r(k,ccl)));
426 for s=ccl
427 Wchain(k,r) = Wchain(k,r) + STeff(k,s) * Uchain_r(k,s) * (1 + SCVchain_in(k,s))/2; % high SCV
428 end
429 otherwise % default
430 Wchain(k,r) = STeff(k,r);
431 end
432 if any(ismember(ocl,r))
433 Wchain(k,r) = Wchain(k,r) + (STeff(k,r) * stationaryQlen(k,r) + STeff(k,sd)*stationaryQlen(k,sd)');
434 else
435 switch options.method
436 %case {'default', 'amva.lin', 'lin', 'amva.qdlin','qdlin'} % Linearizer
437 % Wchain(k,r) = Wchain(k,r) + (STeff(k,r) * selfArvlQlenSeenByClosed(k,r) + STeff(k,sd)*stationaryQlen(k,sd)') + (STeff(k,ccl).*Nchain(ccl)*permute(gamma(r,k,ccl),3:-1:1) - STeff(k,r)*gamma(r,k,r));
438 %case {'default', 'amva.lin', 'lin', 'amva.qdlin', 'qdlin'}
439 % fraction = Qchain_in ./ repmat(Nchain_in, size(Qchain_in, 1), 1);
440 % Wchain(k, r) = Wchain(k, r) + STeff(k, r) * (Nchain_in(ccl) * fraction(k, ccl)' - fraction(k, r)) + STeff(k, r) * (Nchain_in(ccl)*permute(gamma(r,k,ccl),3:-1:1) - gamma(r,k,r));
441 otherwise
442 Wchain(k,r) = Wchain(k,r) + (STeff(k,r) * selfArvlQlenSeenByClosed(k,r) + STeff(k,sd)*stationaryQlen(k,sd)');
443 end
444 end
445 else
446 switch options.config.multiserver
447 case 'softmin'
448 Wchain(k,r) = STeff(k,r); % high SCV
449 if any(ismember(ocl,r))
450 Wchain(k,r) = Wchain(k,r) + STeff(k,r) * stationaryQlen(k,r) * Bk(r) + STeff(k,sd) * (stationaryQlen(k,sd) .* Bk(sd))';
451 else
452 % FCFS approximation + reducing backlog proportionally to server utilizations; somewhat similar to
453 % Rolia-Sevcik - method of layers - Sec IV.
454 switch options.method
455 %case {'default', 'amva.lin', 'lin', 'amva.qdlin','qdlin'} % Linearizer
456 % Wchain(k,r) = Wchain(k,r) + STeff(k,r) * selfArvlQlenSeenByClosed(k,r) * Bk(r) + STeff(k,sd) * (stationaryQlen(k,sd) .* Bk(sd))' + (STeff(k,ccl).*Nchain(ccl)*permute(gamma(r,k,ccl),3:-1:1) - STeff(k,r)*gamma(r,k,r));
457 otherwise
458 Wchain(k,r) = Wchain(k,r) + STeff(k,r) * selfArvlQlenSeenByClosed(k,r) * Bk(r) + STeff(k,sd) * (stationaryQlen(k,sd) .* Bk(sd))';
459 end
460 end
461 case {'default','seidmann'}
462 Wchain(k,r) = STeff(k,r) * (nservers(k)-1); % multi-server correction with serial think time, (1/nservers(k)) term already in STeff
463 Wchain(k,r) = Wchain(k,r) + STeff(k,r); % high SCV
464 if any(ismember(ocl,r))
465 Wchain(k,r) = Wchain(k,r) + (STeff(k,r) * deltaclass(r) * stationaryQlen(k,r)*Bk(r) + STeff(k,sd).*Bk(sd)*stationaryQlen(k,sd)');
466 else
467 % FCFS approximation + reducing backlog proportionally to server utilizations; somewhat similar to
468 % Rolia-Sevcik - method of layers - Sec IV. (1/nservers(k)) term already in STeff
469 switch options.method
470 %case {'default', 'amva.lin', 'lin', 'amva.qdlin','qdlin'} % Linearizer
471 % Wchain(k,r) = Wchain(k,r) + (STeff(k,r) * selfArvlQlenSeenByClosed(k,r)*Bk(r) + STeff(k,sd).*Bk(sd)*stationaryQlen(k,sd)') + (STeff(k,ccl).*Nchain(ccl)*permute(gamma(r,k,ccl),3:-1:1) - STeff(k,r)*gamma(r,k,r));
472 otherwise
473 Wchain(k,r) = Wchain(k,r) + (STeff(k,r) * selfArvlQlenSeenByClosed(k,r)*Bk(r) + STeff(k,sd).*Bk(sd)*stationaryQlen(k,sd)');
474 end
475 end
476 case 'suri'
477 % Suri multiserver: W = S * (1 + L_m * suriFactor)
478 Wchain(k,r) = STeff(k,r);
479 if any(ismember(ocl,r))
480 L_m = deltaclass(r) * stationaryQlen(k,r);
481 for s=sd
482 L_m = L_m + stationaryQlen(k,s);
483 end
484 Wchain(k,r) = Wchain(k,r) + STeff(k,r) * L_m * suriFactor(k);
485 else
486 L_m = selfArvlQlenSeenByClosed(k,r);
487 for s=sd
488 L_m = L_m + stationaryQlen(k,s);
489 end
490 Wchain(k,r) = Wchain(k,r) + STeff(k,r) * L_m * suriFactor(k);
491 end
492 end
493 end
494 end
495
496 case {SchedStrategy.HOL} % non-preemptive priority
497 if STeff(k,r) > 0
498 Uchain_r = Uchain_in ./ repmat(Xchain_in,M,1) .* (repmat(Xchain_in,M,1) + repmat(tau(r,:),M,1));
499
500 switch options.config.np_priority
501 case {'default','cl'} % Chandy-Lakshmi
502 UHigherPrio=0;
503 for h=nnzclasses_hprio{r}
504 UHigherPrio = UHigherPrio + Vchain_in(k,h)*STeff(k,h)*(Xchain_in(h)-Qchain_in(k,h)*tau(h));
505 end
506 prioScaling = min([max([options.tol,1-UHigherPrio]),1-options.tol]);
507 case 'shadow' % Sevcik's shadow server
508 UHigherPrio=0;
509 for h=nnzclasses_hprio{r}
510 UHigherPrio = UHigherPrio + Vchain_in(k,h)*STeff(k,h)*Xchain_in(h);
511 end
512 prioScaling = min([max([options.tol,1-UHigherPrio]),1-options.tol]);
513 end
514
515 if nservers(k)>1
516 if sum(deltaclass .* Xchain_in .* Vchain_in(k,:) .* STeff(k,:)) < 0.75 % light-load case
517 switch options.config.multiserver
518 case 'softmin'
519 Bk = ((deltaclass .* Xchain_in .* Vchain_in(k,:) .* STeff(k,:))); % note: this is in 0-1 as a utilization
520 case {'default','seidmann'}
521 Bk = ((deltaclass .* Xchain_in .* Vchain_in(k,:) .* STeff(k,:)) / nservers(k)); % note: this is in 0-1 as a utilization
522 case 'suri'
523 Bk = ones(1,K); % not used; suriFactor handles multiserver correction
524 end
525 else % high-load case
526 switch options.config.multiserver
527 case 'softmin'
528 Bk = (deltaclass .* Xchain_in .* Vchain_in(k,:) .* STeff(k,:)).^nservers(k); % Rolia
529 case {'default','seidmann'}
530 Bk = (deltaclass .* Xchain_in .* Vchain_in(k,:) .* STeff(k,:) / nservers(k)).^nservers(k); % Rolia
531 case 'suri'
532 Bk = ones(1,K); % not used; suriFactor handles multiserver correction
533 end
534 end
535 else
536 Bk = ones(1,K);
537 end
538
539 hasLjd = ~isempty(ljdscaling) && any(~cellfun(@isempty, ljdscaling));
540 if nservers(k)==1 && (~isempty(lldscaling) || ~isempty(cdscaling) || hasLjd)
541 switch options.config.highvar % high SCV
542 case 'hvmva'
543 Wchain(k,r) = (STeff(k,r) / prioScaling) * (1-sum(Uchain_r(k,ccl)));
544 for s=ccl
545 UHigherPrio_s=0;
546 for h=nnzclasses_hprio{s}
547 UHigherPrio_s = UHigherPrio_s + Vchain_in(k,h)*STeff(k,h)*(Xchain_in(h)-Qchain_in(k,h)*tau(h));
548 end
549 prioScaling_s = min([max([options.tol,1-UHigherPrio_s]),1-options.tol]);
550 Wchain(k,r) = Wchain(k,r) + (STeff(k,s) / prioScaling_s) * Uchain_r(k,s) * (1 + SCVchain_in(k,s))/2; % high SCV correction
551 end
552 otherwise % default
553 Wchain(k,r) = STeff(k,r) / prioScaling;
554 end
555
556 if any(ismember(ocl,r))
557 Wchain(k,r) = Wchain(k,r) + (STeff(k,r) * stationaryQlen(k,r)) / prioScaling;
558 else
559 %switch options.method
560 %case {'default', 'amva.lin', 'lin', 'amva.qdlin','qdlin'} % Linearizer
561 % %Wchain(k,r) = Wchain(k,r) + (STeff(k,r) * selfArvlQlenSeenByClosed(k,r) + STeff(k,sdprio)*stationaryQlen(k,sdprio)') + (STeff(k,[r,sdprio]).*Nchain([r,sdprio])*permute(gamma(r,k,[r,sdprio]),3:-1:1) - STeff(k,r)*gamma(r,k,r));
562 % Wchain(k,r) = Wchain(k,r) + (STeff(k,r) * selfArvlQlenSeenByClosed(k,r) - STeff(k,r)*gamma(r,k,r)) / prioScaling;
563 % otherwise
564 %Wchain(k,r) = Wchain(k,r) + (STeff(k,r) * selfArvlQlenSeenByClosed(k,r) + STeff(k,sdprio)*stationaryQlen(k,sdprio)');
565 Wchain(k,r) = Wchain(k,r) + (STeff(k,r) * selfArvlQlenSeenByClosed(k,r)) / prioScaling;
566 %end
567 end
568 else
569 switch options.config.multiserver
570 case 'softmin'
571 Wchain(k,r) = STeff(k,r) / prioScaling; % high SCV
572 if any(ismember(ocl,r))
573 Wchain(k,r) = Wchain(k,r) + STeff(k,r) * stationaryQlen(k,r) * Bk(r) / prioScaling;
574 else
575 % FCFS approximation + reducing backlog proportionally to server utilizations; somewhat similar to
576 % Rolia-Sevcik - method of layers - Sec IV.
577 %switch options.method
578 %case {'default', 'amva.lin', 'lin', 'amva.qdlin','qdlin'} % Linearizer
579 % %Wchain(k,r) = Wchain(k,r) + STeff(k,r) * selfArvlQlenSeenByClosed(k,r) * Bk(r) + STeff(k,sdprio) * (stationaryQlen(k,sdprio) .* Bk(sdprio))' + (STeff(k,[r,sdprio]).*Nchain([r,sdprio])*permute(gamma(r,k,[r,sdprio]),3:-1:1) - STeff(k,r)*gamma(r,k,r));
580 % Wchain(k,r) = Wchain(k,r) + STeff(k,r) * selfArvlQlenSeenByClosed(k,r) * Bk(r) / prioScaling + (STeff(k,[r]).*Nchain([r])*permute(gamma(r,k,[r]),3:-1:1) - STeff(k,r)*gamma(r,k,r)) / prioScaling;
581 % otherwise
582 %Wchain(k,r) = Wchain(k,r) + STeff(k,r) * selfArvlQlenSeenByClosed(k,r) * Bk(r) + STeff(k,sdprio) * (stationaryQlen(k,sdprio) .* Bk(sdprio))';
583 Wchain(k,r) = Wchain(k,r) + STeff(k,r) * selfArvlQlenSeenByClosed(k,r) * Bk(r) / prioScaling;
584 %end
585 end
586 case {'default','seidmann'}
587 Wchain(k,r) = STeff(k,r) * (nservers(k)-1)/prioScaling; % multi-server correction with serial think time, (1/nservers(k)) term already in STeff
588 Wchain(k,r) = Wchain(k,r) + STeff(k,r)/prioScaling; % high SCV
589 if any(ismember(ocl,r))
590 Wchain(k,r) = Wchain(k,r) + (STeff(k,r) * stationaryQlen(k,r)*Bk(r))/prioScaling;
591 else
592 Wchain(k,r) = Wchain(k,r) + STeff(k,r) * selfArvlQlenSeenByClosed(k,r)*Bk(r)/prioScaling;
593 end
594 case 'suri'
595 % Suri multiserver: W = S * (1 + L_m * suriFactor) / prioScaling
596 Wchain(k,r) = STeff(k,r) / prioScaling;
597 if any(ismember(ocl,r))
598 Wchain(k,r) = Wchain(k,r) + (STeff(k,r) * stationaryQlen(k,r) * suriFactor(k)) / prioScaling;
599 else
600 Wchain(k,r) = Wchain(k,r) + (STeff(k,r) * selfArvlQlenSeenByClosed(k,r) * suriFactor(k)) / prioScaling;
601 end
602 end
603 end
604 end
605
606 end
607 end
608end
609end