1classdef SolverBA < NetworkSolver
2 % Bound Analysis solver
for closed queueing networks.
4 % SolverBA
is a dedicated home
for asymptotic and hierarchical throughput/
5 % queue-length BOUNDS. Unlike SolverMVA (point estimates), each method
6 % returns an optimistic or pessimistic bound;
the .upper/.lower pair
for a
7 % family brackets
the exact solution. Bounds need only demands (
visits x
8 % service time) and populations -- no service-time distributions -- so
the
9 % featset
is deliberately narrow (closed product-form-parameterized models).
12 % Noniterative (SolverBA-native, solver_ba_analyzer):
13 % aba.* Asymptotic Bound Analysis (Denning-Buzen)
14 % bjb.* Balanced Job Bounds (Zahorjan et al.)
15 % pb.* Proportional Bounds (Eager-Sevcik)
16 % gb.* Geometric Bounds (Casale-Muntz-Serazzi)
17 % sb.* Simple bounds from power sums (Harel-Namn-Sturm); NOT a
18 % geometric variant despite sitting beside gb. Rejects delay
19 % stations, as does lr; gb accepts them.
20 % mwba.* Multiclass worst-
case balanced bound
21 % Hierarchical / iterative (SolverBA-native, level-parameterized):
22 % pbh Performance Bound Hierarchy (Eager-Sevcik 1983), level option
23 % sib Successively Improving Bounds (Srinivasan 1985), level option
24 % cbh Convolutional Bound Hierarchies (Dowdy et al. 1984)
25 % bjbk / pbk iterative BJB(k)/PB(k) with delay (Casale et al. 2008)
26 % cub Composite Upper Bound, multiclass upper-only (Kerola 1986)
27 % mbjb multiclass Balanced Job Bounds lower (Kerola eq. 10; seeds cub)
28 % ssd multiServer Disaggregation bounds (Suri-Dallery 1986)
29 % ldbcmp LD-BCMP closed-open equivalence bound (Anselmi-Cremonesi 2008)
30 % LP-based reduction bounds (QRF library, solver_ba_qrf_analyzer):
31 % qr / qrf.mmi Quadratic Reduction Framework (single-class PH)
32 % lr / qrf.mmi.linear Linear Reduction variant
33 % qrf.mem, qrf.mmi.ld, qrf.bas.*, qrf.rsrd further QRF sub-methods
35 % Copyright (c) 2012-2026, Imperial College London
36 % All rights reserved.
39 function self = SolverBA(model, varargin)
40 % SOLVERBA Create a bound-analysis solver instance
41 self@NetworkSolver(model, mfilename);
42 self.setOptions(Solver.parseOptions(varargin, SolverBA.defaultOptions));
46 function sn = getStruct(self)
47 % GETSTRUCT Get model data structure
for analysis
48 sn = self.model.getStruct(
false);
51 [runtime, analyzer] = runAnalyzer(self, options);
53 function bounds = getBounds(self)
54 % GETBOUNDS Return
the {lower,upper} bracket
for the requested
55 % bound family as a
struct with fields Xlower/Xupper (throughput)
56 % and Qlower/Qupper (queue lengths). The current method
's family
57 % prefix (before the first '.
') selects the family; a hierarchical
58 % method uses its own level option.
59 % One-sided families (cub upper-only, mbjb/ldbcmp lower-only)
60 % return NaN on the missing side.
61 fam = self.options.method;
62 dot = strfind(fam,'.
');
64 fam = fam(1:dot(1)-1);
66 valid = self.listValidMethods();
67 Tl = NaN; Ql = NaN; Tu = NaN; Qu = NaN;
68 if any(strcmp([fam,'.lower
'], valid))
69 [Ql,~,~,Tl] = self.solveSide([fam,'.lower
']);
71 if any(strcmp([fam,'.upper
'], valid))
72 [Qu,~,~,Tu] = self.solveSide([fam,'.upper
']);
74 bounds = struct('Tlower
',Tl,'Tupper
',Tu,'Qlower
',Ql,'Qupper
',Qu);
77 function [Q,U,R,T] = solveSide(self, method)
78 % SOLVESIDE Re-run the solver for one side of the bracket.
79 % The re-run instance inherits the CALLER'S FULL OPTION SET (level,
80 % verbose, tol, ...) and only overrides
the method. Constructing it
81 % with just 'method' would silently reset options.level to its
82 % default of 2, so a hierarchical family (pbh/cbh/pbk/bjbk/sib)
83 % reached through getBounds would never tighten as level
is raised.
84 s = SolverBA(self.model);
88 [Q,U,R,T] = s.getAvg();
91 function BoundsTable = getBoundsTable(self, keepDisabled)
92 % GETBOUNDSTABLE Table of
the {lower,upper} bracket per station and
93 %
class, in
the layout of GETAVGTABLE. Columns:
94 %
Station, JobClass, Qlower, Qupper, Tlower, Tupper
95 % One-sided families (cub upper-only, mbjb/ldbcmp lower-only) carry
96 % NaN on
the missing side; NaN
is preserved, never replaced by zero.
100 b = self.getBounds();
101 sn = self.getStruct();
104 Ql = SolverBA.expandBound(b.Qlower, M, K);
105 Qu = SolverBA.expandBound(b.Qupper, M, K);
106 Tl = SolverBA.expandBound(b.Tlower, M, K);
107 Tu = SolverBA.expandBound(b.Tupper, M, K);
108 [Qlval, Quval, Tlval, Tuval] = deal([]);
113 vals = [Ql(ist,k), Qu(ist,k), Tl(ist,k), Tu(ist,k)];
114 finite = vals(~isnan(vals));
115 % Mirror getAvgTable
's drop of disabled station-class pairs,
116 % but NaN-safe: a row is kept when any value that is present
117 % is nonzero, so an all-NaN side never removes the row.
118 if keepDisabled || isempty(finite) || any(finite ~= 0)
119 JobClass{end+1,1} = sn.classnames{k}; %#ok<AGROW>
120 Station{end+1,1} = sn.nodenames{sn.stationToNode(ist)}; %#ok<AGROW>
121 Qlval(end+1) = Ql(ist,k); %#ok<AGROW>
122 Quval(end+1) = Qu(ist,k); %#ok<AGROW>
123 Tlval(end+1) = Tl(ist,k); %#ok<AGROW>
124 Tuval(end+1) = Tu(ist,k); %#ok<AGROW>
128 Station = label(Station);
129 JobClass = label(JobClass);
130 Qlower = Qlval(:); Qupper = Quval(:);
131 Tlower = Tlval(:); Tupper = Tuval(:);
132 BoundsTable = Table(Station, JobClass, Qlower, Qupper, Tlower, Tupper);
133 BoundsTable = IndexedTable(BoundsTable);
136 function [allMethods] = listValidMethods(self)
137 % LISTVALIDMETHODS Valid bound methods for the current model.
138 % Hierarchical methods (pbh/sib/cbh/bjbk/pbk/cub/ssd/ldbcmp) are
139 % appended here as each backing pfqn_* algorithm is integrated.
142 'aba.upper
','aba.lower
', ...
143 'bjb.upper
','bjb.lower
', ...
144 'pb.upper
','pb.lower
', ...
145 'gb.upper
','gb.lower
', ...
146 'sb.upper
','sb.lower
', ...
147 'mwba.upper
','mwba.lower
', ...
148 'pbh.upper
','pbh.lower
', ...
149 'pbk.upper
','pbk.lower
', ...
150 'bjbk.upper
','bjbk.lower
', ...
151 'cbh.upper
','cbh.lower
', ...
152 'ssd.upper
','ssd.lower
', ...
153 'cub.upper
','mbjb.lower
', ...
154 'sib.upper
','sib.lower
', ...
156 'qr
','lr
','lr.upper
','lr.lower
', ...
157 'qrf.mmi
','qrf.mem
','qrf.mmi.ld
','qrf.mmi.linear
', ...
158 'qrf.bas.mmi
','qrf.bas.mem
','qrf.bas
','qrf.rsrd
'};
163 function Y = expandBound(X, M, K)
164 % EXPANDBOUND Normalize a bracket side to an (M,K) matrix. A
165 % one-sided family leaves its missing side as the scalar NaN
166 % returned by getBounds; expand it so the table keeps full shape
167 % with NaN entries rather than erroring or dropping the column.
177 function libs = getLibrariesUsed(sn, options) %#ok<INUSL>
178 % GETLIBRARIESUSED External libraries used by SolverBA. The QRF
179 % reduction bounds solve an LP via the Optimization Toolbox.
181 if ~isempty(options) && isfield(options,'method
') && ...
182 (startsWith(options.method,'qrf
') || any(strcmp(options.method,{'qr
','lr
'})))
183 libs{end+1} = 'MATLAB Optimization Toolbox (fmincon)
';
187 function options = defaultOptions
188 % OPTIONS = DEFAULTOPTIONS()
189 options = SolverOptions('MVA
');
190 options.method = 'default';
191 % Hierarchy level for pbh/cbh (MVA steps / exact-convolved servers)
192 % and iteration count k for pbk/bjbk. Default 2.
196 function [bool, featSupported] = supports(model)
197 % SUPPORTS Whether SolverBA can bound the given model
198 featUsed = model.getUsedLangFeatures();
199 featSupported = SolverFeatureSet();
200 featSupported.setTrue({ ...
201 'ClassSwitch
','DelayStation
','Queue
', ...
202 'Sink
','Source
','Router
', ...
203 'ClosedClass
','OpenClass
', ...
204 'SchedStrategy_INF
','SchedStrategy_PS
', ...
205 'SchedStrategy_FCFS
','SchedStrategy_LCFSPR
', ...
206 'RoutingStrategy_PROB
','RoutingStrategy_RAND
', ...
207 'ClosedClass_multiclass
'});
208 bool = SolverFeatureSet.supports(featSupported, featUsed);