1function [Q,U,R,T,C,X,lG,runtime,iter] = solver_ba_analyzer(sn, options)
2% [Q,U,R,T,C,X,LG,RUNTIME,ITER] = SOLVER_BA_ANALYZER(QN, OPTIONS)
4% Copyright (c) 2012-2026, Imperial College London
14line_debug(
'MVA bound analyzer starting: method=%s, nclasses=%d, njobs=%s', options.method, sn.nclasses, mat2str(sn.njobs));
17 case {
'lr.upper',
'lr.lower'}
18 % LP-based Linear Reduction bound (Casale et al.), solved by linprog
19 % via mapqn_bnd_lr_pf. NOT
the same as
'qrf.mmi.linear': that method
is
20 %
"linear" only in its
explicit Aeq/beq constraint representation,
while
21 % its objective
is the nonlinear MEM entropy solved by fmincon. Both
22 % senses are valid bounds since
the LP relaxation contains
the exact
24 line_debug(
'Using LP linear-reduction bound (%s)', options.method);
25 if sn.nclasses ~= 1 || sn.nclosedjobs <= 0
26 line_error(mfilename,
'Method lr supports single-class closed networks only.');
28 if any(sn.nservers(sn.sched ~= SchedStrategy.INF)>1)
29 line_error(mfilename,'Unsupported method for a model with multi-server stations.');
31 if any(sn.sched == SchedStrategy.INF)
32 line_error(mfilename,'Method lr does not support delay (infinite-server) stations.');
34 if strcmp(options.method,'lr.lower'), sense = 'min'; else, sense = 'max'; end
41 params.mu = sn.rates(:,1);
43 params.r = repmat((V(:)
')/totV, M, 1);
44 params.verbose = false;
47 res = mapqn_bnd_lr_pf(params, ti, sense);
48 U(ti,1) = res.objective;
50 % Chain throughput implied by the bounded utilizations (U_i = X*V_i/mu_i)
51 cand = U(:,1) .* sn.rates(:,1) ./ V;
52 cand = cand(isfinite(cand));
53 if isempty(cand), X(1,1) = 0; else, X(1,1) = min(cand); end
55 if strcmp(sense,'max
')
56 R(:,1) = (1 ./ sn.rates(:,1)) * N;
58 R(:,1) = 1 ./ sn.rates(:,1);
60 Q(:,1) = T(:,1) .* R(:,1);
61 D = V ./ sn.rates(:,1);
62 if strcmp(sense,'max
'), C(1,1) = N*sum(D); else, C(1,1) = sum(D); end
65 line_debug('Using ABA upper bound
');
66 if sn.nclasses==1 && sn.nclosedjobs >0 % closed single-class queueing network
67 if any(sn.nservers(sn.sched ~= SchedStrategy.INF)>1)
68 line_error(mfilename,'Unsupported method for a model with multi-server stations.
');
71 Z = sum(V(sn.sched == SchedStrategy.INF) ./ sn.rates(sn.sched == SchedStrategy.INF));
72 D = V(sn.sched ~= SchedStrategy.INF) ./ sn.rates(sn.sched ~= SchedStrategy.INF);
75 C(1,1) = Z + N * sum(D);
76 X(1,1) = min( 1/Dmax, N / (Z + sum(D)));
78 R(:,1) = 1 ./ sn.rates * N;
79 R(sn.sched == SchedStrategy.INF,1) = 1 ./ sn.rates(sn.sched == SchedStrategy.INF,1);
80 Q(:,1) = T(:,1) .* R(:,1);
81 U(:,1) = T(:,1) ./ sn.rates;
82 U((sn.sched == SchedStrategy.INF),1) = Q((sn.sched == SchedStrategy.INF),1);
83 lG = - N*log(X(1,1)); % approx
87 line_debug('Using ABA lower bound
');
88 if sn.nclasses==1 && sn.nclosedjobs >0 % closed single-class queueing network
89 if any(sn.nservers(sn.sched ~= SchedStrategy.INF)>1)
90 line_error(mfilename,'Unsupported method for a model with multi-server stations.
');
93 Z = sum(V(sn.sched == SchedStrategy.INF) ./ sn.rates(sn.sched == SchedStrategy.INF));
94 D = V(sn.sched ~= SchedStrategy.INF) ./ sn.rates(sn.sched ~= SchedStrategy.INF);
96 X(1,1) = N / (Z + N*sum(D));
99 R(:,1) = 1 ./ sn.rates;
100 Q(:,1) = T(:,1) .* R(:,1);
101 U(:,1) = T(:,1) ./ sn.rates;
102 U((sn.sched == SchedStrategy.INF),1) = Q((sn.sched == SchedStrategy.INF),1);
103 lG = - N*log(X(1,1)); % approx
107 line_debug('Using BJB upper bound
');
108 if sn.nclasses==1 && sn.nclosedjobs >0 % closed single-class queueing network
109 if any(sn.nservers(sn.sched ~= SchedStrategy.INF)>1)
110 line_error(mfilename,'Unsupported method for a model with multi-server stations.
');
113 Z = sum(V(sn.sched == SchedStrategy.INF) ./ sn.rates(sn.sched == SchedStrategy.INF));
114 D = V(sn.sched ~= SchedStrategy.INF) ./ sn.rates(sn.sched ~= SchedStrategy.INF);
117 Xaba_upper_1 = min( 1/Dmax, (N-1) / (Z + sum(D)));
118 Xaba_lower_1 = (N-1) / (Z + (N-1)*sum(D));
119 C(1,1) = (Z+sum(D)+max(D)*(N-1-Z*Xaba_lower_1));
120 X(1,1) = min(1/Dmax, N / (Z+sum(D)+mean(D)*(N-1-Z*Xaba_upper_1)));
121 T(:,1) = V .* X(1,1);
122 % RN undefined in the literature so we use ABA upper
123 R(:,1) = 1 ./ sn.rates * N;
125 %RN(sn.sched ~= SchedStrategy.INF,1) = NaN * D+ max(D) ./ V(sn.sched ~= SchedStrategy.INF) .* (N-1-Z*Xaba_lower_1) / (sn.nstations - sum(sn.sched == SchedStrategy.INF));
126 R(sn.sched == SchedStrategy.INF,1) = 1 ./ sn.rates(sn.sched == SchedStrategy.INF,1);
127 Q(:,1) = T(:,1) .* R(:,1);
128 U(:,1) = T(:,1) ./ sn.rates;
129 U((sn.sched == SchedStrategy.INF),1) = Q((sn.sched == SchedStrategy.INF),1);
130 lG = - N*log(X(1,1)); % approx
134 line_debug('Using BJB lower bound
');
135 if sn.nclasses==1 && sn.nclosedjobs >0 % closed single-class queueing network
136 if any(sn.nservers(sn.sched ~= SchedStrategy.INF)>1)
137 line_error(mfilename,'Unsupported method for a model with multi-server stations.
');
140 Z = sum(V(sn.sched == SchedStrategy.INF) ./ sn.rates(sn.sched == SchedStrategy.INF));
141 D = V(sn.sched ~= SchedStrategy.INF) ./ sn.rates(sn.sched ~= SchedStrategy.INF);
144 Xaba_upper_1 = min( 1/Dmax, (N-1) / (Z + sum(D)));
145 Xaba_lower_1 = (N-1) / (Z + (N-1)*sum(D));
146 C(1,1) = (Z+sum(D)+mean(D)*(N-1-Z*Xaba_upper_1));
147 X(1,1) = N / (Z+sum(D)+max(D)*(N-1-Z*Xaba_lower_1));
148 T(:,1) = V .* X(1,1);
149 % RN undefined in the literature so we use ABA lower
150 R(:,1) = 1 ./ sn.rates;
152 %RN(sn.sched ~= SchedStrategy.INF,1) = NaN * 1 ./ sn.rates(sn.sched ~= SchedStrategy.INF,1) + mean(D) ./ V(sn.sched ~= SchedStrategy.INF) .* (N-1-Z*Xaba_upper_1) / (sn.nstations - sum(sn.sched == SchedStrategy.INF));
153 R(sn.sched == SchedStrategy.INF,1) = 1 ./ sn.rates(sn.sched == SchedStrategy.INF,1);
154 Q(:,1) = T(:,1) .* R(:,1);
155 U(:,1) = T(:,1) ./ sn.rates;
156 U((sn.sched == SchedStrategy.INF),1) = Q((sn.sched == SchedStrategy.INF),1);
157 lG = - N*log(X(1,1)); % approx
161 line_debug('Using PB upper bound
');
162 if sn.nclasses==1 && sn.nclosedjobs >0 % closed single-class queueing network
163 if any(sn.nservers(sn.sched ~= SchedStrategy.INF)>1)
164 line_error(mfilename,'Unsupported method for a model with multi-server stations.
');
167 Z = sum(V(sn.sched == SchedStrategy.INF) ./ sn.rates(sn.sched == SchedStrategy.INF));
168 D = V(sn.sched ~= SchedStrategy.INF) ./ sn.rates(sn.sched ~= SchedStrategy.INF);
171 Xaba_upper_1 = min( 1/Dmax, (N-1) / (Z + sum(D)));
172 Xaba_lower_1 = (N-1) / (Z + (N-1)*sum(D));
173 Dpb2 = sum(D.^2)/sum(D);
174 DpbN = sum(D.^N)/sum(D.^(N-1));
175 C(1,1) = (Z+sum(D)+DpbN*(N-1-Z*Xaba_lower_1));
176 X(1,1) = min(1/Dmax, N / (Z+sum(D)+Dpb2*(N-1-Z*Xaba_upper_1)));
177 T(:,1) = V .* X(1,1);
178 % RN undefined in the literature so we use ABA upper
179 R(:,1) = 1 ./ sn.rates * N;
181 %RN(sn.sched ~= SchedStrategy.INF,1) = NaN * 1 ./ sn.rates(sn.sched ~= SchedStrategy.INF,1) + (D.^N/sum(D.^(N-1))) ./ V(sn.sched ~= SchedStrategy.INF) * (N-1-Z*Xaba_upper_1);
182 R(sn.sched == SchedStrategy.INF,1) = 1 ./ sn.rates(sn.sched == SchedStrategy.INF,1);
183 Q(:,1) = T(:,1) .* R(:,1);
184 U(:,1) = T(:,1) ./ sn.rates;
185 U((sn.sched == SchedStrategy.INF),1) = Q((sn.sched == SchedStrategy.INF),1);
186 lG = - N*log(X(1,1)); % approx
190 line_debug('Using PB lower bound
');
191 if sn.nclasses==1 && sn.nclosedjobs >0 % closed single-class queueing network
192 if any(sn.nservers(sn.sched ~= SchedStrategy.INF)>1)
193 line_error(mfilename,'Unsupported method for a model with multi-server stations.
');
196 Z = sum(V(sn.sched == SchedStrategy.INF) ./ sn.rates(sn.sched == SchedStrategy.INF));
197 D = V(sn.sched ~= SchedStrategy.INF) ./ sn.rates(sn.sched ~= SchedStrategy.INF);
200 Xaba_upper_1 = min( 1/Dmax, (N-1) / (Z + sum(D)));
201 Xaba_lower_1 = (N-1) / (Z + (N-1)*sum(D));
202 Dpb2 = sum(D.^2)/sum(D);
203 DpbN = sum(D.^N)/sum(D.^(N-1));
204 C(1,1) = (Z+sum(D)+Dpb2*(N-1-Z*Xaba_upper_1));
205 X(1,1) = N / (Z+sum(D)+DpbN*(N-1-Z*Xaba_lower_1));
206 T(:,1) = V .* X(1,1);
207 % RN undefined in the literature so we use ABA lower
208 R(:,1) = 1 ./ sn.rates;
210 %RN(sn.sched ~= SchedStrategy.INF,1) = NaN * 1 ./ sn.rates(sn.sched ~= SchedStrategy.INF,1) + (D.^2/sum(D)) ./ V(sn.sched ~= SchedStrategy.INF) * (N-1-Z*Xaba_upper_1);
211 R(sn.sched == SchedStrategy.INF,1) = 1 ./ sn.rates(sn.sched == SchedStrategy.INF,1);
212 Q(:,1) = T(:,1) .* R(:,1);
213 U(:,1) = T(:,1) ./ sn.rates;
214 U((sn.sched == SchedStrategy.INF),1) = Q((sn.sched == SchedStrategy.INF),1);
215 lG = - N*log(X(1,1)); % approx
219 line_debug('Using SB upper bound
');
220 if sn.nclasses==1 && sn.nclosedjobs >0 % closed single-class queueing network
221 if any(sn.nservers(sn.sched ~= SchedStrategy.INF)>1)
222 line_error(mfilename,'Unsupported method for a model with multi-server stations.
');
224 if any(sn.sched == SchedStrategy.INF)
225 line_error(mfilename,'Unsupported method for a model with infinite-server stations.
');
228 D = V(sn.sched ~= SchedStrategy.INF) ./ sn.rates(sn.sched ~= SchedStrategy.INF);
229 Z = sum(V(sn.sched == SchedStrategy.INF) ./ sn.rates(sn.sched == SchedStrategy.INF));
235 C(1,1) = Z+A1+(N-1)*(A1*A2+A3)/(A1^2+A2);
236 X(1,1) = min([1/Dmax,N / (Z+A1+(N-1)*(A1*A2+A3)/(A1^2+A2))]);
237 T(:,1) = V .* X(1,1);
238 % RN undefined in the literature so we use ABA lower
239 R(:,1) = 1 ./ sn.rates;
241 %RN(sn.sched ~= SchedStrategy.INF,1) = NaN * 1 ./ sn.rates(sn.sched ~= SchedStrategy.INF,1) + (D.^2/sum(D)) ./ V(sn.sched ~= SchedStrategy.INF) * (N-1-Z*Xaba_upper_1);
242 R(sn.sched == SchedStrategy.INF,1) = 1 ./ sn.rates(sn.sched == SchedStrategy.INF,1);
243 Q(:,1) = T(:,1) .* R(:,1);
244 U(:,1) = T(:,1) ./ sn.rates;
245 U((sn.sched == SchedStrategy.INF),1) = Q((sn.sched == SchedStrategy.INF),1);
246 lG = - N*log(X(1,1)); % approx
250 line_debug('Using SB lower bound
');
251 if sn.nclasses==1 && sn.nclosedjobs >0 % closed single-class queueing network
252 if any(sn.nservers(sn.sched ~= SchedStrategy.INF)>1)
253 line_error(mfilename,'Unsupported method for a model with multi-server stations.
');
255 if any(sn.sched == SchedStrategy.INF)
256 line_error(mfilename,'Unsupported method for a model with infinite-server stations.
');
259 D = V(sn.sched ~= SchedStrategy.INF) ./ sn.rates(sn.sched ~= SchedStrategy.INF);
260 Z = sum(V(sn.sched == SchedStrategy.INF) ./ sn.rates(sn.sched == SchedStrategy.INF));
264 C(1,1) = Z+A1+(N-1)*(AN/A1)^(1/(N-1));
265 X(1,1) = N / (Z+A1+(N-1)*(AN/A1)^(1/(N-1)));
266 T(:,1) = V .* X(1,1);
267 % RN undefined in the literature so we use ABA lower
268 R(:,1) = 1 ./ sn.rates;
270 %RN(sn.sched ~= SchedStrategy.INF,1) = NaN * 1 ./ sn.rates(sn.sched ~= SchedStrategy.INF,1) + (D.^2/sum(D)) ./ V(sn.sched ~= SchedStrategy.INF) * (N-1-Z*Xaba_upper_1);
271 R(sn.sched == SchedStrategy.INF,1) = 1 ./ sn.rates(sn.sched == SchedStrategy.INF,1);
272 Q(:,1) = T(:,1) .* R(:,1);
273 U(:,1) = T(:,1) ./ sn.rates;
274 U((sn.sched == SchedStrategy.INF),1) = Q((sn.sched == SchedStrategy.INF),1);
275 lG = - N*log(X(1,1)); % approx
279 line_debug('Using GB upper bound
');
280 if sn.nclasses==1 && sn.nclosedjobs >0 % closed single-class queueing network
281 if any(sn.nservers(sn.sched ~= SchedStrategy.INF)>1)
282 line_error(mfilename,'Unsupported method for a model with multi-server stations.
');
285 Z = sum(V(sn.sched == SchedStrategy.INF) ./ sn.rates(sn.sched == SchedStrategy.INF));
286 D = V(sn.sched ~= SchedStrategy.INF) ./ sn.rates(sn.sched ~= SchedStrategy.INF);
289 X(1,1) = min(1/Dmax, pfqn_xzgsbup(D,N,Z));
290 C(1,1) = N / pfqn_xzgsblow(D,N,Z);
291 T(:,1) = V .* X(1,1);
292 XNlow = pfqn_xzgsblow(D,N,Z);
295 if sn.sched(i) == SchedStrategy.INF
296 R(i,1) = 1 / sn.rates(i);
297 Q(i,1) = X(1,1) * R(i,1);
300 Q(i,1) = pfqn_qzgbup(D,N,Z,k);
301 R(i,1) = Q(i,1) / XNlow / V(i) ;
304 R(sn.sched == SchedStrategy.INF,1) = 1 ./ sn.rates(sn.sched == SchedStrategy.INF,1);
305 U(:,1) = T(:,1) ./ sn.rates;
306 U((sn.sched == SchedStrategy.INF),1) = Q((sn.sched == SchedStrategy.INF),1);
307 lG = - N*log(X(1,1)); % approx
311 line_debug('Using GB lower bound
');
312 if sn.nclasses==1 && sn.nclosedjobs >0 % closed single-class queueing network
313 if any(sn.nservers(sn.sched ~= SchedStrategy.INF)>1)
314 line_error(mfilename,'Unsupported method for a model with multi-server stations.
');
317 Z = sum(V(sn.sched == SchedStrategy.INF) ./ sn.rates(sn.sched == SchedStrategy.INF));
318 D = V(sn.sched ~= SchedStrategy.INF) ./ sn.rates(sn.sched ~= SchedStrategy.INF);
320 X(1,1) = pfqn_xzgsblow(D,N,Z);
321 C(1,1) = N / pfqn_xzgsbup(D,N,Z);
322 T(:,1) = V .* X(1,1);
323 XNup = pfqn_xzgsbup(D,N,Z);
326 if sn.sched(i) == SchedStrategy.INF
327 R(i,1) = 1 / sn.rates(i);
328 Q(i,1) = X(1,1) * R(i,1);
331 Q(i,1) = pfqn_qzgblow(D,N,Z,k);
332 R(i,1) = Q(i,1) / XNup / V(i) ;
335 U(:,1) = T(:,1) ./ sn.rates;
336 U((sn.sched == SchedStrategy.INF),1) = Q((sn.sched == SchedStrategy.INF),1);
337 lG = - N*log(X(1,1)); % approx
340 case {'mwba.upper
','mwba.lower
'}
341 line_debug(['Using Majumdar-Woodside robust box bound:
' options.method]);
342 if sn.nclosedjobs > 0 && ~any(isinf(sn.njobs)) % fully closed multiclass network
343 if any(sn.nservers(sn.sched ~= SchedStrategy.INF) > 1)
344 line_error(mfilename,'Unsupported method for a model with multi-server stations.
');
346 [Lchain,STchain,Vchain,alpha,Nchain] = sn_get_demands_chain(sn);
347 isdelay = (sn.sched == SchedStrategy.INF);
348 Zc = sum(Lchain(isdelay,:),1); % think time per chain
349 Vq = Vchain(~isdelay,:); % queueing stations
350 Sq = STchain(~isdelay,:);
351 % map station scheduling to MW discipline codes
352 % 0=FIFO, 1=PS, 2=non-preemptive priority, 3=preemptive priority
353 qstat = find(~isdelay);
354 schedq = zeros(numel(qstat),1);
355 for kk=1:numel(qstat)
356 schedq(kk) = mwrbb_disc_code(sn.sched(qstat(kk)));
358 % per-chain priority (lower value = higher priority); use the
359 % reference class priority of each chain
360 prioc = zeros(1,sn.nchains);
362 inch = sn.inchain{c};
363 if isfield(sn,'refclass
') && ~isempty(sn.refclass) && sn.refclass(c)>0
364 prioc(c) = sn.classprio(sn.refclass(c));
366 prioc(c) = min(sn.classprio(inch));
369 [Xlo,Xup,Wlo] = pfqn_mwrbb(Vq,Sq,Nchain,Zc,schedq,prioc);
370 if strcmp(options.method,'mwba.upper
')
376 Tchain = zeros(sn.nstations,sn.nchains);
377 Uchain = zeros(sn.nstations,sn.nchains);
378 Qchain = zeros(sn.nstations,sn.nchains);
381 Tchain(i,c) = Xchain(c) * Vchain(i,c);
382 Uchain(i,c) = Xchain(c) * Lchain(i,c); % utilization law
384 Qchain(i,c) = Xchain(c) * Lchain(i,c);
387 if strcmp(options.method,'mwba.upper
')
388 w = Sq(kk,c); % no-contention residence
390 w = Wlo(kk,c); % Theorem 1 residence
392 Qchain(i,c) = Xchain(c) * Vchain(i,c) * w;
396 [Q,U,R,T,C,X] = sn_deaggregate_chain_results(sn, Lchain, [], STchain, Vchain, alpha, Qchain, Uchain, [], Tchain, [], Xchain);
400 case {'pbh.upper
','pbh.lower
'}
401 line_debug(['Using PBH (Eager-Sevcik) bound:
' options.method]);
402 [V,Zt,D,N] = ba_sc_demands(sn, options.method);
403 ba_reject_multiserver(sn, options.method);
404 [Xlo,Xhi] = pfqn_pbh(D, N, Zt, ba_level(options));
405 up = strcmp(options.method,'pbh.upper
');
406 [Q,U,R,T,C,lG] = ba_fill(sn, V, N, up*Xhi+(~up)*Xlo, Zt, D, up);
408 case {'cbh.upper
','cbh.lower
'}
409 line_debug(['Using CBH (Dowdy) bound:
' options.method]);
410 [V,Zt,D,N] = ba_sc_demands(sn, options.method);
411 ba_reject_multiserver(sn, options.method);
412 [Xlo,Xhi] = pfqn_cbh(D, N, Zt, ba_level(options));
413 up = strcmp(options.method,'cbh.upper
');
414 [Q,U,R,T,C,lG] = ba_fill(sn, V, N, up*Xhi+(~up)*Xlo, Zt, D, up);
416 case {'pbk.upper
','pbk.lower
'}
417 line_debug(['Using PB(k) bound:
' options.method]);
418 [V,Zt,D,N] = ba_sc_demands(sn, options.method);
419 ba_reject_multiserver(sn, options.method);
420 [Xlo,Xhi] = pfqn_pbk(D, N, Zt, ba_level(options));
421 up = strcmp(options.method,'pbk.upper
');
422 [Q,U,R,T,C,lG] = ba_fill(sn, V, N, up*Xhi+(~up)*Xlo, Zt, D, up);
424 case {'bjbk.upper
','bjbk.lower
'}
425 line_debug(['Using BJB(k) bound:
' options.method]);
426 [V,Zt,D,N] = ba_sc_demands(sn, options.method);
427 ba_reject_multiserver(sn, options.method);
428 [Xlo,Xhi] = pfqn_bjbk(D, N, Zt, ba_level(options));
429 up = strcmp(options.method,'bjbk.upper
');
430 [Q,U,R,T,C,lG] = ba_fill(sn, V, N, up*Xhi+(~up)*Xlo, Zt, D, up);
432 case {'ssd.upper
','ssd.lower
'}
433 line_debug(['Using SSD (Suri-Dallery) multiserver bound:
' options.method]);
434 [V,Zt,D,N] = ba_sc_demands(sn, options.method);
435 cvec = sn.nservers(sn.sched ~= SchedStrategy.INF);
436 [Xlo,Xhi] = pfqn_ssd(D, N, Zt, cvec(:));
437 up = strcmp(options.method,'ssd.upper
');
438 [Q,U,R,T,C,lG] = ba_fill(sn, V, N, up*Xhi+(~up)*Xlo, Zt, D, up);
440 case {'cub.upper
','mbjb.lower
'}
441 % cub.upper: Kerola composite upper bound (upper-only in the paper).
442 % mbjb.lower: the multiclass Balanced Job Bounds lower bound (Kerola
443 % eq. 10) that seeds it; the natural multiclass counterpart of bjb.
444 line_debug(['Using multiclass composite/BJB bound:
' options.method]);
445 if sn.nclosedjobs <= 0 || any(isinf(sn.njobs))
446 line_error(mfilename,'Method
''%s
'' supports fully closed networks only.
', options.method);
448 [Lchain,STchain,Vchain,alpha,Nchain] = sn_get_demands_chain(sn);
449 isdelay = (sn.sched == SchedStrategy.INF);
450 Zc = sum(Lchain(isdelay,:),1);
451 Lq = Lchain(~isdelay,:);
452 [Xub,Xlb] = pfqn_mcub(Lq, Nchain, Zc);
453 up = strcmp(options.method,'cub.upper
');
454 Xchain = up*Xub(:)' + (~up)*Xlb(:)
';
455 Tchain = zeros(sn.nstations,sn.nchains);
456 Uchain = zeros(sn.nstations,sn.nchains);
457 Qchain = zeros(sn.nstations,sn.nchains);
459 Tchain(:,c) = Xchain(c) * Vchain(:,c);
460 Uchain(:,c) = Xchain(c) * Lchain(:,c); % utilization law
461 Qchain(:,c) = Uchain(:,c); % coarse (bound on X is primary)
463 [Q,U,R,T,C,X] = sn_deaggregate_chain_results(sn, Lchain, [], STchain, Vchain, alpha, Qchain, Uchain, [], Tchain, [], Xchain);
466 case {'sib.upper
','sib.lower
'}
467 line_debug(['Using SIB (Srinivasan) bound:
' options.method]);
468 [V,Zt,D,N] = ba_sc_demands(sn, options.method);
469 ba_reject_multiserver(sn, options.method);
471 line_error(mfilename,'Method
''%s
'' supports Z=0 (no delay station) only; delay needs
the SIB Section-3.2 extension.
', options.method);
473 [Xlo,Xhi] = pfqn_sib(D, N, 0, ba_level(options));
474 up = strcmp(options.method,'sib.upper
');
475 [Q,U,R,T,C,lG] = ba_fill(sn, V, N, up*Xhi+(~up)*Xlo, Zt, D, up);
477 case {'ldbcmp.lower
'}
478 line_debug(['Using LD-BCMP (Anselmi-Cremonesi) lower throughput bound:
' options.method]);
479 [V,Zt,D,N] = ba_sc_demands(sn, options.method);
480 % Fixed-rate / delay parameterization (Heffes c=0); LD stations are
481 % treated as fixed-rate at their limiting demand.
482 [Xlo,~,Qhat] = pfqn_ldbcmp(D, N, Zt, zeros(numel(D),1));
484 line_error(mfilename,'Method
''%s
'' requires the asymptotic regime N >= Qhat (Qhat=%.4f > N=%d).
', options.method, Qhat, N);
486 [Q,U,R,T,C,lG] = ba_fill(sn, V, N, Xlo, Zt, D, false);
491% Single-class demand extraction for the hierarchical bound cases: returns
492% visit vector V, aggregate think time Zt, per-queue demand vector D and the
493% closed population N. Rejects non-single-class models with a clear error.
494function [V,Zt,D,N] = ba_sc_demands(sn, method)
495if sn.nclasses ~= 1 || sn.nclosedjobs <= 0
496 line_error('solver_ba_analyzer
', ...
497 'Method
''%s
'' supports single-
class closed networks only.', method);
500isinf_ = (sn.sched == SchedStrategy.INF);
501Zt = sum(V(isinf_) ./ sn.rates(isinf_));
502D = V(~isinf_) ./ sn.rates(~isinf_);
506function ba_reject_multiserver(sn, method)
507if any(sn.nservers(sn.sched ~= SchedStrategy.INF) > 1)
508 line_error('solver_ba_analyzer', ...
509 'Method ''%s'' does not support multi-server stations (use ''ssd'').', method);
513function lvl = ba_level(options)
515if isfield(options,'level') && ~isempty(options.level)
520% Fill per-station [Q,U,R,T,C] from a scalar chain-throughput bound X, using
521%
the same optimistic (isUpper) / pessimistic residence construction as
the
522% ABA bound so conventions match across
the bound families.
523function [Q,U,R,T,C,lG] = ba_fill(sn, V, N, X, Zt, D, isUpper)
524isinf_ = (sn.sched == SchedStrategy.INF);
527 R = (1 ./ sn.rates(:)) * N;
528 R(isinf_) = 1 ./ sn.rates(isinf_);
531 R = 1 ./ sn.rates(:);
536U(isinf_) = Q(isinf_);
540% Map a SchedStrategy
id to a Majumdar-Woodside discipline code:
541% 0=FIFO, 1=PS, 2=non-preemptive priority, 3=preemptive priority,
542% 4=ABA full-contention (discipline-independent).
543function code = mwrbb_disc_code(s)
545 case {SchedStrategy.FCFS}
546 code = 0; % FIFO (Theorem 1 / Lemma 1)
547 case {SchedStrategy.PS, SchedStrategy.DPS, SchedStrategy.GPS, ...
548 SchedStrategy.PSPRIO, SchedStrategy.DPSPRIO, SchedStrategy.GPSPRIO}
549 code = 1; % processor sharing (Lemma 2)
550 case {SchedStrategy.HOL} % HOL == FCFSPRIO: non-preemptive priority
551 code = 2; % non-preemptive priority (Lemmas 4-5)
552 case {SchedStrategy.FCFSPRPRIO, SchedStrategy.LCFSPRPRIO}
553 code = 3; % preemptive-resume priority (Lemma 3)
555 code = 4; % non-FCFS work-conserving: ABA full-contention bound