1% Example 15: Bound analysis with SolverBA
3% A bounding solver answers a different question from SolverMVA. Instead of a
4% single point estimate it returns one guaranteed side of an interval, and the
5% .lower/.upper pair of a family brackets the exact solution. Bounds need only
6% the service demands and the population, never the service distributions, so
7% they are cheap enough to sit inside an optimization loop where a full solve
10% The model
is a closed network with a think Delay and two Queues of unequal
11% speed, so that the bottleneck
is well defined.
14N = 5; % number of jobs in the closed chain
15model = Network(
'BoundsDemo');
16delay = Delay(model,
'Think');
17q1 = Queue(model,
'Q1', SchedStrategy.PS);
18q2 = Queue(model,
'Q2', SchedStrategy.PS);
19jobs = ClosedClass(model,
'C', N, delay);
20delay.setService(jobs, Exp(1/2)); % think time Z = 2
21q1.setService(jobs, Exp(1/1.0)); % demand D1 = 1.0
22q2.setService(jobs, Exp(1/1.5)); % demand D2 = 1.5 (bottleneck)
23model.link(Network.serialRouting(delay,q1,q2));
25%% Block 2: the exact reference
26% The throughput at the reference station
is the system throughput of the
27% closed chain, and
is what every bound below brackets.
28Xexact = MVA(model,
'method',
'exact').getAvgTable().Tput(1);
30%% Block 3: the bounds table
31% getBoundsTable
is the counterpart of getAvgTable: it reports the bracket per
32% station and
class, with columns Qlower/Qupper and Tlower/Tupper. A single
33% call evaluates both sides of the family of the selected method.
34gbTable = SolverBA(model,
'method',
'gb.upper').getBoundsTable()
36%% Block 4: comparing families
37% getBounds
is the programmatic accessor, returning the raw bracket rather
38% than a formatted table. aba uses only the bottleneck demand and the total
39% demand and
is the crudest; bjb and gb exploit more structure. Which family
40%
is sharpest
is model dependent, so it
is worth comparing several.
41fprintf(
'\n%-6s %12s %12s %12s\n',
'family',
'Tlower',
'Texact',
'Tupper');
42for family = {
'aba',
'bjb',
'gb'}
43 b = SolverBA(model,
'method',[family{1}
'.upper']).getBounds();
44 fprintf(
'%-6s %12.6f %12.6f %12.6f\n', family{1}, ...
45 b.Tlower(1), Xexact, b.Tupper(1));
48%% Block 5: a bound hierarchy tightening with the level option
49% Hierarchical families are parameterized by options.level: raising it spends
50% more work and returns a tighter pair. The Eager-Sevcik hierarchy pbh becomes
51% exact once the level reaches the population, so the bracket width falls to
53fprintf(
'\n%-6s %12s %12s %12s\n',
'level',
'Tlower',
'Tupper',
'width');
55 b = SolverBA(model,
'method',
'pbh.upper',
'level',level).getBounds();
56 fprintf(
'%-6d %12.6f %12.6f %12.6f\n', level, ...
57 b.Tlower(1), b.Tupper(1), b.Tupper(1)-b.Tlower(1));
60%% Block 6: one-sided families
61% Not every family
is two-sided. cub
is upper-only and mbjb and ldbcmp are
62% lower-only, so the missing side
is reported as NaN rather than as zero,
63% which keeps
"no bound" distinguishable from
"the bound is zero".
64cubTable = SolverBA(model,
'method',
'cub.upper').getBoundsTable()
66% listValidMethods reports every method name the solver accepts. Some carry
67% structural restrictions beyond the feature set: sb and sib are delay-free,
68% and lr additionally
requires a single-server single-
class closed model, so
69% on the model above they raise an error rather than
return a wrong answer.
70fprintf(
'SolverBA advertises %d methods.\n', ...
71 numel(SolverBA(model).listValidMethods()));