LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
renv_lqn_twostages.m
1function renv_lqn_twostages()
2% RENV_LQN_TWOSTAGES LayeredNetwork operating in a two-stage random environment.
3%
4% Demonstrates SolverENV running over a LayeredNetwork (LQN) base model, using
5% the uniform model/solver interface (no branching in ENV). The environment
6% alternates between an UP stage (fast database) and a DOWN stage (slow
7% database); the environment-averaged total throughput must lie between the two
8% single-stage LQN solutions.
9
10warning off;
11
12%% Two structurally identical LQN stages, differing only in DB host demand.
13upModel = buildLQN('LQN_UP', 0.8); % fast DB activity
14downModel = buildLQN('LQN_DOWN', 3.0); % slow DB activity (degraded)
15
16%% Random environment: UP <-> DOWN
17env = Environment('DBReliability');
18env.addStage('UP', 'operational', upModel);
19env.addStage('DOWN', 'degraded', downModel);
20env.addTransition('UP', 'DOWN', Exp(0.2)); % mean UP time = 5
21env.addTransition('DOWN', 'UP', Exp(1.0)); % mean DOWN time = 1
22env.init();
23
24%% Solve with SolverENV over SolverLN(.,@SolverFluid)
25% The transient window is set on SolverLN (not the layer factory): the layered
26% fixed-point iteration solves each layer in steady state, and SolverLN applies
27% the timespan only to the per-layer transient getTranAvg call.
28T = 50;
29fldFactory = @(mm) SolverFluid(mm, 'verbose', false);
30lnFactory = @(m) SolverLN(m, fldFactory, 'timespan', [0, T], 'verbose', false);
31
32options = SolverENV.defaultOptions;
33options.iter_max = 20;
34options.iter_tol = 0.02;
35options.verbose = false;
36
37envSolver = SolverENV(env, lnFactory, options);
38[QN, UN, RN, TN] = envSolver.getAvg(); %#ok<ASGLU>
39fprintf('ENV over LQN ran: aggregate size = %d x %d\n', size(QN,1), size(QN,2));
40envAvgTable = envSolver.getAvgTable(); %#ok<NASGU>
41disp(envAvgTable);
42
43%% Single-stage aggregate references (same block-diagonal layout)
44[upQ, upT] = stageAggregate(upModel, fldFactory, T);
45[downQ, downT] = stageAggregate(downModel, fldFactory, T);
46
47%% Verify
48% (1) The solver runs and returns finite, population-conserving metrics.
49assert(all(isfinite(QN(:))), 'ENV aggregate Q contains non-finite values');
50assert(abs(sum(QN(:)) - sum(upQ(:))) < 1e-2 && abs(sum(QN(:)) - sum(downQ(:))) < 1e-2, ...
51 'ENV aggregate does not conserve the closed population of the stages');
52
53% (2) A physically monotone scalar (total throughput) must lie between the two
54% single-stage solutions: a slower database slows the whole system, so the
55% environment-averaged throughput brackets the UP and DOWN values.
56% Disabled station/class cells carry NaN (no throughput defined); they
57% contribute zero to the system total, matching the stage references.
58Xup = sum(upT(:)); Xdown = sum(downT(:)); Xenv = sum(TN(isfinite(TN)));
59lo = min(Xup, Xdown); hi = max(Xup, Xdown);
60tol = 1e-2 * max(1, hi);
61fprintf('Total throughput UP=%.4f DOWN=%.4f ENV=%.4f\n', Xup, Xdown, Xenv);
62fprintf('Aggregate Q (sum) UP=%.4f DOWN=%.4f ENV=%.4f\n', sum(upQ(:)), sum(downQ(:)), sum(QN(:)));
63assert(Xenv >= lo - tol && Xenv <= hi + tol, ...
64 'ENV-averaged throughput is not bracketed by the single-stage solutions');
65
66% (3) Quantitative coupling: the env-averaged throughput increases with the
67% stationary probability of the fast UP stage, P(UP)=b/(a+b) for switch rates
68% a (UP->DOWN) and b (DOWN->UP). Both settings remain within the bracket.
69Xlow = env2StageTput(1.0, 0.2, T); % P(UP)=0.167 (mostly slow DOWN)
70Xhigh = env2StageTput(0.2, 1.0, T); % P(UP)=0.833 (mostly fast UP)
71fprintf('Monotonicity P(UP)=0.167 -> %.4f P(UP)=0.833 -> %.4f\n', Xlow, Xhigh);
72assert(Xhigh > Xlow + 1e-3, 'ENV throughput is not monotone in P(UP)');
73assert(Xlow >= lo - tol && Xhigh <= hi + tol, 'ENV throughputs escape the single-stage bracket');
74
75% (4) Three-stage environment (UP/MID/DOWN) stays bracketed by the extreme
76% single-stage solutions (exercises the E>2 coupling).
77X3 = env3StageTput(T);
78fprintf('Three-stage ENV throughput = %.4f (bracket [%.4f, %.4f])\n', X3, lo, hi);
79assert(X3 >= lo - tol && X3 <= hi + tol, 'Three-stage ENV-averaged throughput is not bracketed');
80
81fprintf('PASS: ENV-over-LQN meanfield ran; throughput bracketed, monotone in P(UP), 3-stage bracketed.\n');
82end
83
84function X = env2StageTput(a, b, T)
85% Env-averaged total throughput for the two-stage UP/DOWN environment at switch
86% rates a (UP->DOWN) and b (DOWN->UP).
87upModel = buildLQN('UP', 0.8);
88downModel = buildLQN('DOWN', 3.0);
89env = Environment('R');
90env.addStage('UP', 'operational', upModel);
91env.addStage('DOWN', 'degraded', downModel);
92env.addTransition('UP', 'DOWN', Exp(a));
93env.addTransition('DOWN', 'UP', Exp(b));
94env.init();
95X = envTputSum(env, T);
96end
97
98function X = env3StageTput(T)
99% Env-averaged total throughput for a three-stage UP/MID/DOWN environment.
100upModel = buildLQN('UP', 0.8);
101midModel = buildLQN('MID', 1.6);
102downModel = buildLQN('DOWN', 3.0);
103env = Environment('R3');
104env.addStage('UP', 'operational', upModel);
105env.addStage('MID', 'degraded', midModel);
106env.addStage('DOWN', 'failed', downModel);
107env.addTransition('UP', 'MID', Exp(0.3));
108env.addTransition('MID', 'DOWN', Exp(0.3));
109env.addTransition('DOWN', 'MID', Exp(0.6));
110env.addTransition('MID', 'UP', Exp(0.6));
111env.init();
112X = envTputSum(env, T);
113end
114
115function X = envTputSum(env, T)
116% Solve an LQN-in-ENV and return the finite aggregate throughput sum.
117fldFactory = @(mm) SolverFluid(mm, 'verbose', false);
118lnFactory = @(m) SolverLN(m, fldFactory, 'timespan', [0, T], 'verbose', false);
119opt = SolverENV.defaultOptions;
120opt.iter_max = 10; opt.iter_tol = 0.03; opt.verbose = false;
121s = SolverENV(env, lnFactory, opt);
122[~,~,~,TN] = s.getAvg();
123X = sum(TN(isfinite(TN)));
124end
125
126function model = buildLQN(name, dbMean)
127model = LayeredNetwork(name);
128P1 = Processor(model, 'ClientProcessor', 1, SchedStrategy.PS); %#ok<NASGU>
129P2 = Processor(model, 'DBProcessor', 1, SchedStrategy.PS); %#ok<NASGU>
130T1 = Task(model, 'ClientTask', 5, SchedStrategy.REF).on(P1);
131T1.setThinkTime(Exp.fitMean(5.0));
132T2 = Task(model, 'DBTask', Inf, SchedStrategy.INF).on(P2);
133E1 = Entry(model, 'ClientEntry').on(T1);
134E2 = Entry(model, 'DBEntry').on(T2);
135A1 = Activity(model, 'ClientActivity', Exp.fitMean(1.0)).on(T1);
136A1.boundTo(E1).synchCall(E2, 2.5);
137A2 = Activity(model, 'DBActivity', Exp.fitMean(dbMean)).on(T2);
138A2.boundTo(E2).repliesTo(E2);
139end
140
141function [Q, T] = stageAggregate(model, fldFactory, Tspan)
142% Steady transient aggregate (block-diagonal M x K) queue lengths and
143% throughputs for one LQN stage, using the same SolverLN layout SolverENV
144% consumes.
145s = SolverLN(model, fldFactory, 'timespan', [0, Tspan], 'verbose', false);
146[Qt,~,Tt] = s.getTranAvg();
147M = size(Qt,1); K = size(Qt,2);
148Q = zeros(M,K); T = zeros(M,K);
149for i=1:M
150 for k=1:K
151 Q(i,k) = tailValue(Qt{i,k});
152 T(i,k) = tailValue(Tt{i,k});
153 end
154end
155end
156
157function v = tailValue(cell_ik)
158v = 0;
159if isstruct(cell_ik) && isfield(cell_ik,'metric') && ~isempty(cell_ik.metric)
160 v = cell_ik.metric(end);
161end
162end
Definition Station.m:245