1function [ahat, info] = infer_lqn_ekf(hfun, a0, P0, Z, Q, R, options)
2% INFER_LQN_EKF Extended Kalman Filter
for LQN parameter identification.
4% [AHAT, INFO] = INFER_LQN_EKF(HFUN, A0, P0, Z, Q, R, OPTIONS) tracks a
5% hidden parameter vector across
the measurement sequence Z
using an Extended
6% Kalman Filter, following Zheng, Yang, Woodside, Litoiu, Iszlai,
"Tracking
7% Time-Varying Parameters in Software Systems with Extended Kalman Filters",
8% CASCON 2005 (equations 1-9). The parameter
is modelled as a zero-mean
9% random walk a_k = a_{k-1} + w and
the measurement as z_k = h(a_k) + v,
10% where h
is the (nonlinear) LQN performance model supplied as HFUN.
13% HFUN : handle mapping a parameter vector to a predicted observation
14% vector z = h(a) (evaluates
the LQN model).
15% A0 : (np x 1) initial parameter estimate.
16% P0 : (np x np) initial estimation-error covariance.
17% Z : (no x nsteps) measurement matrix, one
column per step.
18% Q : (np x np) parameter-drift (process-noise) covariance.
19% R : (no x no) measurement-error covariance.
20% OPTIONS : struct with optional fields:
21% fdStep (1e-3), fdFloor (1e-6) - finite-difference steps
22% clampPositive (true) - clamp estimates to > fdFloor
23% aTrue ([]) - ground truth for Ea metric
27% AHAT : (np x nsteps) parameter estimate trajectory.
28% INFO : struct with fields
P (final covariance), Phist (per-step
29% covariances), e (no x nsteps prediction errors), zpred (predicted
30% measurements), Er (prediction RMS), Ea (parameter tracking RMS vs
31% aTrue when supplied, else []).
33% Copyright (c) 2012-2026, Imperial College London
36if nargin < 7, options = struct(); end
37fdStep = infer_lqn_optget(options,
'fdStep', 1e-3);
38fdFloor = infer_lqn_optget(options,
'fdFloor', 1e-6);
39clampPositive = infer_lqn_optget(options,
'clampPositive',
true);
40aTrue = infer_lqn_optget(options,
'aTrue', []);
41verbose = infer_lqn_optget(options,
'verbose',
false);
48ahat = zeros(np, nsteps);
49info.e = zeros(no, nsteps);
50info.zpred = zeros(no, nsteps);
51info.Phist = cell(1, nsteps);
57 % (1) predict: zero-mean drift keeps a_pred = a; project covariance (eq 5)
61 % (2,4) predicted measurement and sensitivity matrix H = dh/da at a_pred
62 [H, zpred] = infer_lqn_jacobian(hfun, aPred, fdStep, fdFloor);
64 % (3) prediction error
68 % (6) Kalman gain (suboptimal because h
is nonlinear)
69 S = H * Ppred * H
' + R;
72 % (4-update) improved parameter estimate
78 % (7) covariance update; symmetrize
for numerical stability
79 P = (I_np - K * H) * Ppred;
84 info.zpred(:, k) = zpred;
88 line_printf('[infer_lqn_ekf] step %d/%d ||e||=%.4g\n
', k, nsteps, norm(e));
93% RMS tracking (Ea) and prediction (Er) errors (paper Section 4)
94info.Er = sqrt(mean(info.e(:).^2));
97 D = ahat - repmat(aTrue, 1, nsteps);
98 info.Ea = sqrt(mean(D(:).^2));