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 = min(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 otherwise
207 line_error(mfilename,'Unrecognized multiserver approximation method');
208end
209
210Wchain = zeros(M,K);
211
212STeff = zeros(size(STchain_in)); % effective service time
213lldterm = repmat(lldterm, 1, K);
214% ljdterm is already (M, K) - contains either LJD or LJCD values
215for r=nnzclasses
216 for k=1:M
217 STeff(k,r) = STchain_in(k,r) * lldterm(k,r) * msterm(k) * cdterm(k,r) * ljdterm(k,r);
218 end
219end
220
221%% if amva.qli or amva.fli, update now totArvlQlenSeenByClosed with STeff
222switch options.method
223 case 'qli' % Wang-Sevcik queue line
224 infset = sched == SchedStrategy.INF;
225 for k=1:M
226 switch sched(k)
227 case {SchedStrategy.HOL}
228 for r = nnzclasses
229 if Nchain_in(r) == 1
230 totArvlQlenSeenByClosed(k,r) = sum(Qchain_in(k,nnzclasses_ehprio{r})) - Qchain_in(k,r);
231 else
232 qlinum = STeff(k,r) * (1+sum(Qchain_in(k,nnzclasses_ehprio{r})) - Qchain_in(k,r));
233 qliden = sum(STeff(infset,r));
234 for m=1:M
235 qliden = qliden + STeff(m,r) * (1+sum(Qchain_in(m,nnzclasses_ehprio{r})) - Qchain_in(m,r));
236 end
237 totArvlQlenSeenByClosed(k,r) = sum(Qchain_in(k,nnzclasses_ehprio{r})) - (1/(Nchain_in(r)-1))*(Qchain_in(k,r) - qlinum/qliden);
238 end
239 end
240 otherwise
241 for r = nnzclasses
242 if Nchain_in(r) == 1
243 totArvlQlenSeenByClosed(k,r) = sum(Qchain_in(k,nnzclasses)) - Qchain_in(k,r);
244 else
245 qlinum = STeff(k,r) * (1+sum(Qchain_in(k,nnzclasses)) - Qchain_in(k,r));
246 qliden = sum(STeff(infset,r));
247 for m=1:M
248 qliden = qliden + STeff(m,r) * (1+sum(Qchain_in(m,nnzclasses)) - Qchain_in(m,r));
249 end
250 totArvlQlenSeenByClosed(k,r) = sum(Qchain_in(k,nnzclasses)) - (1/(Nchain_in(r)-1))*(Qchain_in(k,r) - qlinum/qliden);
251 end
252 end
253 end
254 end
255 case 'fli' % Wang-Sevcik fraction line
256 infset = sched == SchedStrategy.INF;
257 for k=1:M
258 switch sched(k)
259 case {SchedStrategy.HOL}
260 for r = nnzclasses
261 if Nchain_in(r) == 1
262 totArvlQlenSeenByClosed(k,r) = sum(Qchain_in(k,nnzclasses_ehprio{r})) - Qchain_in(k,r);
263 else
264 qlinum = STeff(k,r) * (1+sum(Qchain_in(k,nnzclasses_ehprio{r})) - Qchain_in(k,r));
265 qliden = sum(STeff(infset,r));
266 for m=1:M
267 qliden = qliden + STeff(m,r) * (1+sum(Qchain_in(m,nnzclasses_ehprio{r})) - Qchain_in(m,r));
268 end
269 totArvlQlenSeenByClosed(k,r) = sum(Qchain_in(k,nnzclasses_ehprio{r})) - (2/Nchain_in(r))*Qchain_in(k,r) + qlinum/qliden;
270 end
271 end
272 otherwise
273 for r = nnzclasses
274 if Nchain_in(r) == 1
275 totArvlQlenSeenByClosed(k,r) = sum(Qchain_in(k,nnzclasses)) - Qchain_in(k,r);
276 else
277 qlinum = STeff(k,r) * (1+sum(Qchain_in(k,nnzclasses)) - Qchain_in(k,r));
278 qliden = sum(STeff(infset,r));
279 for m=1:M
280 qliden = qliden + STeff(m,r) * (1+sum(Qchain_in(m,nnzclasses)) - Qchain_in(m,r));
281 end
282 totArvlQlenSeenByClosed(k,r) = sum(Qchain_in(k,nnzclasses)) - (2/Nchain_in(r))*Qchain_in(k,r) + qlinum/qliden;
283 end
284 end
285
286 end
287 end
288end
289
290%% compute response times from current queue-lengths
291for ir=1:length(nnzclasses)
292 r=nnzclasses(ir);
293 sd = nnzclasses;
294 sd(ir)=[];
295 %sdprio = setdiff(nnzclasses_ehprio{r},r);
296
297 for k=1:M
298
299 switch sched(k)
300 case SchedStrategy.INF
301 Wchain(k,r) = STeff(k,r);
302
303 case SchedStrategy.PS
304 switch options.method
305 case {'qd', 'lin', 'qdlin'} % QD-LIN interpolation
306 switch options.config.multiserver
307 case 'seidmann' % in this case, qd handles only lld and cd scalings
308 Wchain(k,r) = STeff(k,r) * (nservers(k)-1); % multi-server correction with serial think time, (1/nservers(k)) term already in STeff
309 if ismember(r,ocl)
310 Wchain(k,r) = Wchain(k,r) + STeff(k,r) * (1 + totArvlQlenSeenByOpen(r,k));
311 else
312 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));
313 end
314 case {'default','softmin'}
315 if ismember(r,ocl)
316 Wchain(k,r) = STeff(k,r) * (1 + totArvlQlenSeenByOpen(r,k));
317 else
318 switch options.method
319 case {'lin', 'qdlin'} % Linearizer
320 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));
321 otherwise
322 Wchain(k,r) = STeff(k,r) * (1 + interpTotArvlQlen(k) + (Nt-1)*gamma(r,k));
323 end
324 end
325 end
326 otherwise
327 switch options.config.multiserver
328 case 'seidmann'
329 Wchain(k,r) = STeff(k,r) * (nservers(k)-1); % multi-server correction with serial think time, (1/nservers(k)) term already in STeff
330 if ismember(r,ocl)
331 Wchain(k,r) = Wchain(k,r) + STeff(k,r) * (1 + totArvlQlenSeenByOpen(r,k));
332 else
333 Wchain(k,r) = Wchain(k,r) + STeff(k,r) * (1 + totArvlQlenSeenByClosed(k) + (Nt-1)*gamma(r,k));
334 end
335 case {'default','softmin'}
336 if ismember(r,ocl)
337 Wchain(k,r) = Wchain(k,r) + STeff(k,r) * (1 + totArvlQlenSeenByOpen(r,k));
338 else
339 Wchain(k,r) = Wchain(k,r) + STeff(k,r) * (1 + totArvlQlenSeenByClosed(k) + (Nt-1)*gamma(r,k));
340 end
341 end
342 end
343
344 case SchedStrategy.DPS
345 if nservers(k)>1
346 line_error(mfilename,'Multi-server DPS not supported yet in AMVA solver.')
347 else
348 w = schedparam; % DPS weight
349 tss = Inf; % time-scale separation threshold, this was originally at 5, presently it is disabled
350 %Uhiprio(k,r) = sum(Uchain(k,w(k,:)>tss*w(k,r))); % disabled at the moment
351 %STeff(k,r) = STeff(k,r) / (1-Uhiprio(k,r));
352
353 Wchain(k,r) = STeff(k,r) * (1 + selfArvlQlenSeenByClosed(k,r)); % class-r
354 for s=sd % slowdown due to classes s!=r
355 if w(k,s) == w(k,r) % handle gracefully 0/0 case
356 Wchain(k,r) = Wchain(k,r) + STeff(k,r) * stationaryQlen(k,s);
357 elseif w(k,s)/w(k,r)<=tss
358 Wchain(k,r) = Wchain(k,r) + STeff(k,r) * stationaryQlen(k,s) * w(k,s)/w(k,r);
359 elseif w(k,s)/w(k,r)>tss
360 % if there is time-scale separation, do nothing
361 % all is accounted for by 1/(1-Uhiprio)
362 end
363 end
364 end
365
366 case {SchedStrategy.FCFS, SchedStrategy.SIRO, SchedStrategy.LCFSPR}
367 if STeff(k,r) > 0
368 Uchain_r = Uchain_in ./ repmat(Xchain_in,M,1) .* (repmat(Xchain_in,M,1) + repmat(tau(r,:),M,1));
369
370 if nservers(k)>1
371 deltaclass_r = ones(size(Xchain_in));
372 deltaclass_r(r) = deltaclass(r);
373 if sum(deltaclass_r .* Xchain_in .* Vchain_in(k,:) .* STeff(k,:)) < 0.75 % light-load case
374 Bk = ((deltaclass_r .* Xchain_in .* Vchain_in(k,:) .* STeff(k,:))); % note: this is in 0-1 as a utilization
375 else % high-load case
376 Bk = (deltaclass_r .* Xchain_in .* Vchain_in(k,:) .* STeff(k,:)).^(nservers(k)-1);
377 end
378 else
379 Bk = ones(1,K);
380 end
381
382 hasLjd = ~isempty(ljdscaling) && any(~cellfun(@isempty, ljdscaling));
383 if nservers(k)==1 && (~isempty(lldscaling) || ~isempty(cdscaling) || hasLjd)
384 switch options.config.highvar % high SCV
385 case 'hvmva'
386 Wchain(k,r) = STeff(k,r) * (1-sum(Uchain_r(k,ccl)));
387 for s=ccl
388 Wchain(k,r) = Wchain(k,r) + STeff(k,s) * Uchain_r(k,s) * (1 + SCVchain_in(k,s))/2; % high SCV
389 end
390 otherwise % default
391 Wchain(k,r) = STeff(k,r);
392 end
393 if any(ismember(ocl,r))
394 Wchain(k,r) = Wchain(k,r) + (STeff(k,r) * stationaryQlen(k,r) + STeff(k,sd)*stationaryQlen(k,sd)');
395 else
396 switch options.method
397 %case {'default', 'amva.lin', 'lin', 'amva.qdlin','qdlin'} % Linearizer
398 % 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));
399 %case {'default', 'amva.lin', 'lin', 'amva.qdlin', 'qdlin'}
400 % fraction = Qchain_in ./ repmat(Nchain_in, size(Qchain_in, 1), 1);
401 % 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));
402 otherwise
403 Wchain(k,r) = Wchain(k,r) + (STeff(k,r) * selfArvlQlenSeenByClosed(k,r) + STeff(k,sd)*stationaryQlen(k,sd)');
404 end
405 end
406 else
407 switch options.config.multiserver
408 case 'softmin'
409 Wchain(k,r) = STeff(k,r); % high SCV
410 if any(ismember(ocl,r))
411 Wchain(k,r) = Wchain(k,r) + STeff(k,r) * stationaryQlen(k,r) * Bk(r) + STeff(k,sd) * (stationaryQlen(k,sd) .* Bk(sd))';
412 else
413 % FCFS approximation + reducing backlog proportionally to server utilizations; somewhat similar to
414 % Rolia-Sevcik - method of layers - Sec IV.
415 switch options.method
416 %case {'default', 'amva.lin', 'lin', 'amva.qdlin','qdlin'} % Linearizer
417 % 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));
418 otherwise
419 Wchain(k,r) = Wchain(k,r) + STeff(k,r) * selfArvlQlenSeenByClosed(k,r) * Bk(r) + STeff(k,sd) * (stationaryQlen(k,sd) .* Bk(sd))';
420 end
421 end
422 case {'default','seidmann'}
423 Wchain(k,r) = STeff(k,r) * (nservers(k)-1); % multi-server correction with serial think time, (1/nservers(k)) term already in STeff
424 Wchain(k,r) = Wchain(k,r) + STeff(k,r); % high SCV
425 if any(ismember(ocl,r))
426 Wchain(k,r) = Wchain(k,r) + (STeff(k,r) * deltaclass(r) * stationaryQlen(k,r)*Bk(r) + STeff(k,sd).*Bk(sd)*stationaryQlen(k,sd)');
427 else
428 % FCFS approximation + reducing backlog proportionally to server utilizations; somewhat similar to
429 % Rolia-Sevcik - method of layers - Sec IV. (1/nservers(k)) term already in STeff
430 switch options.method
431 %case {'default', 'amva.lin', 'lin', 'amva.qdlin','qdlin'} % Linearizer
432 % 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));
433 otherwise
434 Wchain(k,r) = Wchain(k,r) + (STeff(k,r) * selfArvlQlenSeenByClosed(k,r)*Bk(r) + STeff(k,sd).*Bk(sd)*stationaryQlen(k,sd)');
435 end
436 end
437 end
438 end
439 end
440
441 case {SchedStrategy.HOL} % non-preemptive priority
442 if STeff(k,r) > 0
443 Uchain_r = Uchain_in ./ repmat(Xchain_in,M,1) .* (repmat(Xchain_in,M,1) + repmat(tau(r,:),M,1));
444
445 switch options.config.np_priority
446 case {'default','cl'} % Chandy-Lakshmi
447 UHigherPrio=0;
448 for h=nnzclasses_hprio{r}
449 UHigherPrio = UHigherPrio + Vchain_in(k,h)*STeff(k,h)*(Xchain_in(h)-Qchain_in(k,h)*tau(h));
450 end
451 prioScaling = min([max([options.tol,1-UHigherPrio]),1-options.tol]);
452 case 'shadow' % Sevcik's shadow server
453 UHigherPrio=0;
454 for h=nnzclasses_hprio{r}
455 UHigherPrio = UHigherPrio + Vchain_in(k,h)*STeff(k,h)*Xchain_in(h);
456 end
457 prioScaling = min([max([options.tol,1-UHigherPrio]),1-options.tol]);
458 end
459
460 if nservers(k)>1
461 if sum(deltaclass .* Xchain_in .* Vchain_in(k,:) .* STeff(k,:)) < 0.75 % light-load case
462 switch options.config.multiserver
463 case 'softmin'
464 Bk = ((deltaclass .* Xchain_in .* Vchain_in(k,:) .* STeff(k,:))); % note: this is in 0-1 as a utilization
465 case {'default','seidmann'}
466 Bk = ((deltaclass .* Xchain_in .* Vchain_in(k,:) .* STeff(k,:)) / nservers(k)); % note: this is in 0-1 as a utilization
467 end
468 else % high-load case
469 switch options.config.multiserver
470 case 'softmin'
471 Bk = (deltaclass .* Xchain_in .* Vchain_in(k,:) .* STeff(k,:)).^nservers(k); % Rolia
472 case {'default','seidmann'}
473 Bk = (deltaclass .* Xchain_in .* Vchain_in(k,:) .* STeff(k,:) / nservers(k)).^nservers(k); % Rolia
474 end
475 end
476 else
477 Bk = ones(1,K);
478 end
479
480 hasLjd = ~isempty(ljdscaling) && any(~cellfun(@isempty, ljdscaling));
481 if nservers(k)==1 && (~isempty(lldscaling) || ~isempty(cdscaling) || hasLjd)
482 switch options.config.highvar % high SCV
483 case 'hvmva'
484 Wchain(k,r) = (STeff(k,r) / prioScaling) * (1-sum(Uchain_r(k,ccl)));
485 for s=ccl
486 UHigherPrio_s=0;
487 for h=nnzclasses_hprio{s}
488 UHigherPrio_s = UHigherPrio_s + Vchain_in(k,h)*STeff(k,h)*(Xchain_in(h)-Qchain_in(k,h)*tau(h));
489 end
490 prioScaling_s = min([max([options.tol,1-UHigherPrio_s]),1-options.tol]);
491 Wchain(k,r) = Wchain(k,r) + (STeff(k,s) / prioScaling_s) * Uchain_r(k,s) * (1 + SCVchain_in(k,s))/2; % high SCV correction
492 end
493 otherwise % default
494 Wchain(k,r) = STeff(k,r) / prioScaling;
495 end
496
497 if any(ismember(ocl,r))
498 Wchain(k,r) = Wchain(k,r) + (STeff(k,r) * stationaryQlen(k,r)) / prioScaling;
499 else
500 %switch options.method
501 %case {'default', 'amva.lin', 'lin', 'amva.qdlin','qdlin'} % Linearizer
502 % %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));
503 % Wchain(k,r) = Wchain(k,r) + (STeff(k,r) * selfArvlQlenSeenByClosed(k,r) - STeff(k,r)*gamma(r,k,r)) / prioScaling;
504 % otherwise
505 %Wchain(k,r) = Wchain(k,r) + (STeff(k,r) * selfArvlQlenSeenByClosed(k,r) + STeff(k,sdprio)*stationaryQlen(k,sdprio)');
506 Wchain(k,r) = Wchain(k,r) + (STeff(k,r) * selfArvlQlenSeenByClosed(k,r)) / prioScaling;
507 %end
508 end
509 else
510 switch options.config.multiserver
511 case 'softmin'
512 Wchain(k,r) = STeff(k,r) / prioScaling; % high SCV
513 if any(ismember(ocl,r))
514 Wchain(k,r) = Wchain(k,r) + STeff(k,r) * stationaryQlen(k,r) * Bk(r) / prioScaling;
515 else
516 % FCFS approximation + reducing backlog proportionally to server utilizations; somewhat similar to
517 % Rolia-Sevcik - method of layers - Sec IV.
518 %switch options.method
519 %case {'default', 'amva.lin', 'lin', 'amva.qdlin','qdlin'} % Linearizer
520 % %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));
521 % 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;
522 % otherwise
523 %Wchain(k,r) = Wchain(k,r) + STeff(k,r) * selfArvlQlenSeenByClosed(k,r) * Bk(r) + STeff(k,sdprio) * (stationaryQlen(k,sdprio) .* Bk(sdprio))';
524 Wchain(k,r) = Wchain(k,r) + STeff(k,r) * selfArvlQlenSeenByClosed(k,r) * Bk(r) / prioScaling;
525 %end
526 end
527 case {'default','seidmann'}
528 Wchain(k,r) = STeff(k,r) * (nservers(k)-1)/prioScaling; % multi-server correction with serial think time, (1/nservers(k)) term already in STeff
529 Wchain(k,r) = Wchain(k,r) + STeff(k,r)/prioScaling; % high SCV
530 if any(ismember(ocl,r))
531 %Wchain(k,r) = Wchain(k,r) + (STeff(k,r) * stationaryQlen(k,r)*Bk(r) + STeff(k,sdprio).*Bk(sdprio)*stationaryQlen(k,sdprio)');
532 Wchain(k,r) = Wchain(k,r) + (STeff(k,r) * stationaryQlen(k,r)*Bk(r))/prioScaling;
533 else
534 % FCFS approximation + reducing backlog proportionally to server utilizations; somewhat similar to
535 % Rolia-Sevcik - method of layers - Sec IV. (1/nservers(k)) term already in STeff
536 %switch options.method
537 %case {'default', 'amva.lin', 'lin', 'amva.qdlin','qdlin'} % Linearizer
538 % %Wchain(k,r) = Wchain(k,r) + (STeff(k,r) * selfArvlQlenSeenByClosed(k,r)*Bk(r) + STeff(k,sdprio).*Bk(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));
539 % 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;
540 % otherwise
541 %Wchain(k,r) = Wchain(k,r) + (STeff(k,r) * selfArvlQlenSeenByClosed(k,r)*Bk(r) + STeff(k,sdprio).*Bk(sdprio)*stationaryQlen(k,sdprio)');
542 Wchain(k,r) = Wchain(k,r) + STeff(k,r) * selfArvlQlenSeenByClosed(k,r)*Bk(r)/prioScaling;
543 %end
544 end
545 end
546 end
547 end
548
549 end
550 end
551end
552end