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()));