1% LQN_PARAMIDENT Identify hidden LQN parameters from measured performance.
3% Demonstrates infer_lqn, an Extended Kalman Filter that tracks hidden LQN
4% parameters (host demands, think times) from measurable performance data,
5% following Zheng, Yang, Woodside, Litoiu, Iszlai,
"Tracking Time-Varying
6% Parameters in Software Systems with Extended Kalman Filters", CASCON 2005.
8% Here two parameters are hidden:
the reference-task think time (
the paper
's Z)
9% and the P2 host demand of activity AS3 (the paper's service demand S_d). The
10% measurable vector
is [R(E1), U(P1), U(P2)] (a user response time and two
11% processor utilizations). We synthesise a measurement sequence with a step
12% change plus noise, then recover
the parameter trajectory.
17% ---- base LQN (mirrors lqn_basic.m) ----------------------------------------
18model = LayeredNetwork(
'paramident_LQN');
19P1 = Processor(model,
'P1', 2, SchedStrategy.PS);
20P2 = Processor(model,
'P2', 3, SchedStrategy.PS);
21T1 = Task(model,
'T1', 50, SchedStrategy.REF).on(P1).setThinkTime(Exp(1/2));
22T2 = Task(model,
'T2', 50, SchedStrategy.FCFS).on(P1).setThinkTime(Exp(1/3));
23T3 = Task(model,
'T3', 25, SchedStrategy.FCFS).on(P2).setThinkTime(Exp(1/4));
24E1 = Entry(model,
'E1').on(T1);
25E2 = Entry(model,
'E2').on(T2);
26E3 = Entry(model,
'E3').on(T3);
27A1 = Activity(model,
'AS1', Exp(10)).on(T1).boundTo(E1).synchCall(E2, 1);
28A2 = Activity(model,
'AS2', Exp(20)).on(T2).boundTo(E2).synchCall(E3, 5).repliesTo(E2);
29A3 = Activity(model,
'AS3', Exp(50)).on(T3).boundTo(E3).repliesTo(E3);
31% ---- what to estimate (paramSpec) and what
is observed (obsSpec) -----------
32paramSpec =
struct(
'type', {
'think',
'hostdem'},
'name', {
'T1',
'AS3'});
33obsSpec =
struct(
'metric', {
'RespT',
'Util',
'Util'},
'name', {
'E1',
'P1',
'P2'});
35% ---- ground-truth parameter trajectory with a step change ------------------
37aTrueSeq = zeros(2, nsteps);
38aTrueSeq(1, :) = 0.5; % think time Z
39aTrueSeq(2, :) = 1/50; % S_d = AS3 mean host demand
40aTrueSeq(1, 13:end) = 1.0; % step: think time doubles at step 13
41aTrueSeq(2, 7:18) = 1/25; % pulse: S_d doubles
for steps 7..18
43% ---- synthesise
the measurement matrix Z from
the true model + noise -------
44solveropts = SolverLN.defaultOptions; solveropts.verbose = VerboseLevel.SILENT;
48 infer_lqn_setparams(model, paramSpec, aTrueSeq(:, k));
49 [QN, UN, RN, TN] = SolverLN(model, solveropts).getEnsembleAvg();
50 lsn = model.getStruct();
51 ztrue = infer_lqn_getobs(lsn.names, ...
52 struct(
'QLen', QN,
'Util', UN,
'RespT', RN,
'Tput', TN), obsSpec);
53 Z(:, k) = ztrue .* (1 + 0.02 * randn(no, 1)); % ~2% measurement noise
56% ---- run
the EKF identification --------------------------------------------
58opt.a0 = [0.7; 1/40]; % deliberately wrong initial guess
59opt.QFac = 0.1; % eq 9a drift-noise
factor
60opt.RFac = 0.2; % eq 9b measurement-noise
factor
61opt.gammaT = 1; % T = T* (measurement interval == system constant)
62opt.aTrue = aTrueSeq(:, end);
63[estModel, info] = infer_lqn(model, paramSpec, obsSpec, Z, opt);
65% ---- report -----------------------------------------------------------------
66fprintf(
'\nStep | Z_true Z_hat | Sd_true Sd_hat | ||e||\n');
68 fprintf(
'%4d | %6.3f %6.3f | %7.4f %7.4f | %.3g\n', k, ...
69 aTrueSeq(1, k), info.ahat(1, k), aTrueSeq(2, k), info.ahat(2, k), ...
72fprintf(
'\nFinal estimate: Z = %.4f (true %.4f), Sd = %.5f (true %.5f)\n', ...
73 info.ahat(1, end), aTrueSeq(1, end), info.ahat(2, end), aTrueSeq(2, end));
74fprintf(
'RMS parameter tracking error Ea = %.4g, prediction error Er = %.4g\n', ...