Skip to content

Commit 1fe117f

Browse files
authored
Merge pull request #469 from nkbranigan/fix_filter_speed
Fix compile slowdown in filtering
2 parents 09a6fc5 + 466f9a8 commit 1fe117f

5 files changed

Lines changed: 28 additions & 42 deletions

File tree

‎dynamax/generalized_gaussian_ssm/inference.py‎

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ def conditional_moments_gaussian_filter(model_params: ParamsGGSSM,
320320
# Emission distribution
321321
emission_dist = model_params.emission_dist
322322

323-
def _step(carry, t, predict=True):
323+
def _step(carry, t):
324324
"""One step of the CMGF"""
325325
ll, pred_mean, pred_cov = carry
326326

@@ -334,17 +334,15 @@ def _step(carry, t, predict=True):
334334
ll += log_likelihood
335335

336336
# Predict the next state
337-
if predict:
338-
Q = _get_params(model_params.dynamics_covariance, 2, t + 1)
339-
pred_mean, pred_cov, _ = _predict(filtered_mean, filtered_cov, f, Q, inputs[t + 1], g_ev, g_cov)
337+
t_next = jnp.minimum(t + 1, num_timesteps - 1)
338+
Q = _get_params(model_params.dynamics_covariance, 2, t_next)
339+
pred_mean, pred_cov, _ = _predict(filtered_mean, filtered_cov, f, Q, inputs[t_next], g_ev, g_cov)
340340

341341
return (ll, pred_mean, pred_cov), (filtered_mean, filtered_cov)
342342

343343
# Run the general linearization filter
344344
carry = (0.0, model_params.initial_mean, model_params.initial_covariance)
345-
carry, outputs = lax.scan(_step, carry, jnp.arange(num_timesteps - 1))
346-
(ll, _, _), final_outputs = _step(carry, num_timesteps - 1, predict=False)
347-
filtered_means, filtered_covs = [jnp.concatenate((a, b[None])) for a, b in zip(outputs, final_outputs)]
345+
(ll, _, _), (filtered_means, filtered_covs) = lax.scan(_step, carry, jnp.arange(num_timesteps))
348346
return PosteriorGSSMFiltered(marginal_loglik=ll,
349347
filtered_means=filtered_means,
350348
filtered_covariances=filtered_covs)

‎dynamax/linear_gaussian_ssm/inference.py‎

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -496,7 +496,7 @@ def _log_likelihood(pred_mean, pred_cov, H, D, d, R, u, y):
496496
return MVNLowRank(m, R, L).log_prob(y)
497497

498498

499-
def _step(carry, t, predict=True):
499+
def _step(carry, t):
500500
"""Run one step of the Kalman filter."""
501501
ll, pred_mean, pred_cov = carry
502502

@@ -512,18 +512,15 @@ def _step(carry, t, predict=True):
512512
filtered_mean, filtered_cov = _condition_on(pred_mean, pred_cov, H, D, d, R, u, y)
513513

514514
# Predict the next state
515-
if predict:
516-
F, B, b, Q = _get_params(params, num_timesteps, t + 1)[:4]
517-
pred_mean, pred_cov = _predict(filtered_mean, filtered_cov, F, B, b, Q, inputs[t + 1])
515+
t_next = jnp.minimum(t + 1, num_timesteps - 1)
516+
F, B, b, Q = _get_params(params, num_timesteps, t_next)[:4]
517+
pred_mean, pred_cov = _predict(filtered_mean, filtered_cov, F, B, b, Q, inputs[t_next])
518518

519519
return (ll, pred_mean, pred_cov), (filtered_mean, filtered_cov)
520520

521521
# Run the Kalman filter
522522
carry = (0.0, params.initial.mean, params.initial.cov)
523-
carry, (filtered_means, filtered_covs) = lax.scan(_step, carry, jnp.arange(num_timesteps - 1))
524-
(ll, _, _), (filtered_mean, filtered_cov) = _step(carry, num_timesteps - 1, predict=False)
525-
filtered_means = jnp.concatenate((filtered_means, filtered_mean[None]))
526-
filtered_covs = jnp.concatenate((filtered_covs, filtered_cov[None]))
523+
(ll, _, _), (filtered_means, filtered_covs) = lax.scan(_step, carry, jnp.arange(num_timesteps))
527524
return PosteriorGSSMFiltered(marginal_loglik=ll, filtered_means=filtered_means, filtered_covariances=filtered_covs)
528525

529526

‎dynamax/linear_gaussian_ssm/info_inference.py‎

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ def lgssm_info_filter(
192192
if param is not None and not callable(param) and param.ndim == ndim + 1:
193193
assert param.shape[0] == num_timesteps
194194
inputs = jnp.zeros((num_timesteps, 0)) if inputs is None else inputs
195-
def _filter_step(carry, t, predict=True):
195+
def _filter_step(carry, t):
196196
"""Run a single step of the Kalman filter."""
197197
ll, pred_eta, pred_prec = carry
198198

@@ -212,22 +212,19 @@ def _filter_step(carry, t, predict=True):
212212
filtered_eta, filtered_prec = _info_condition_on(pred_eta, pred_prec, H, R_prec, D, u, d, y)
213213

214214
# Predict the next state
215-
if predict:
216-
F = _get_params(params.dynamics_weights, 2, t + 1)
217-
Q_prec = _get_params(params.dynamics_precision, 2, t + 1)
218-
B = _get_params(params.dynamics_input_weights, 2, t + 1)
219-
b = _get_params(params.dynamics_bias, 1, t + 1)
220-
pred_eta, pred_prec = _info_predict(filtered_eta, filtered_prec, F, Q_prec, B, inputs[t + 1], b)
215+
t_next = jnp.minimum(t + 1, num_timesteps - 1)
216+
F = _get_params(params.dynamics_weights, 2, t_next)
217+
Q_prec = _get_params(params.dynamics_precision, 2, t_next)
218+
B = _get_params(params.dynamics_input_weights, 2, t_next)
219+
b = _get_params(params.dynamics_bias, 1, t_next)
220+
pred_eta, pred_prec = _info_predict(filtered_eta, filtered_prec, F, Q_prec, B, inputs[t_next], b)
221221

222222
return (ll, pred_eta, pred_prec), (filtered_eta, filtered_prec)
223223

224224
# Run the Kalman filter
225225
initial_eta = params.initial_precision @ params.initial_mean
226226
carry = (0.0, initial_eta, params.initial_precision)
227-
carry, (filtered_etas, filtered_precisions) = lax.scan(_filter_step, carry, jnp.arange(num_timesteps - 1))
228-
(ll, _, _), (filtered_eta, filtered_prec) = _filter_step(carry, num_timesteps - 1, predict=False)
229-
filtered_etas = jnp.concatenate((filtered_etas, filtered_eta[None]))
230-
filtered_precisions = jnp.concatenate((filtered_precisions, filtered_prec[None]))
227+
(ll, _, _), (filtered_etas, filtered_precisions) = lax.scan(_filter_step, carry, jnp.arange(num_timesteps))
231228
return PosteriorGSSMInfoFiltered(marginal_loglik=ll, filtered_etas=filtered_etas, filtered_precisions=filtered_precisions)
232229

233230

‎dynamax/nonlinear_gaussian_ssm/inference_ekf.py‎

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ def extended_kalman_filter(params: ParamsNLGSSM,
121121
f, h, F, H = (_process_fn(fn, inputs) for fn in (f, h, F, H))
122122
inputs = _process_input(inputs, num_timesteps)
123123

124-
def _step(carry, t, predict=True):
124+
def _step(carry, t):
125125
"""Iteratively update the state estimate and log likelihood."""
126126
ll, pred_mean, pred_cov = carry
127127

@@ -138,10 +138,9 @@ def _step(carry, t, predict=True):
138138
filtered_mean, filtered_cov = _condition_on(pred_mean, pred_cov, h, H, R, u, y, num_iter)
139139

140140
# Predict the next state
141-
if predict:
142-
Q = _get_params(params.dynamics_covariance, 2, t + 1)
143-
u_next = inputs[t + 1] if not predict_final else u
144-
pred_mean, pred_cov = _predict(filtered_mean, filtered_cov, f, F, Q, u_next)
141+
t_next = jnp.minimum(t + 1, num_timesteps - 1)
142+
Q = _get_params(params.dynamics_covariance, 2, t_next)
143+
pred_mean, pred_cov = _predict(filtered_mean, filtered_cov, f, F, Q, inputs[t_next])
145144

146145
# Build carry and output states
147146
carry = (ll, pred_mean, pred_cov)
@@ -158,9 +157,7 @@ def _step(carry, t, predict=True):
158157

159158
# Run the extended Kalman filter
160159
carry = (0.0, params.initial_mean, params.initial_covariance)
161-
carry, outputs = lax.scan(_step, carry, jnp.arange(num_timesteps - 1))
162-
(ll, _, _), final_outputs = _step(carry, num_timesteps - 1, predict=predict_final)
163-
outputs = {key: jnp.concatenate((val, final_outputs[key][None])) for key, val in outputs.items()}
160+
(ll, _, _), outputs = lax.scan(_step, carry, jnp.arange(num_timesteps))
164161
if not predict_final:
165162
outputs = {key: val[:-1] if key.startswith("predicted") else val for key, val in outputs.items()}
166163
outputs = {"marginal_loglik": ll, **outputs}

‎dynamax/nonlinear_gaussian_ssm/inference_ukf.py‎

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ def unscented_kalman_filter(params: ParamsNLGSSM,
199199
f, h = (_process_fn(fn, inputs) for fn in (f, h))
200200
inputs = _process_input(inputs, num_timesteps)
201201

202-
def _step(carry, t, predict=True):
202+
def _step(carry, t):
203203
"""One step of the UKF"""
204204
ll, pred_mean, pred_cov = carry
205205

@@ -217,10 +217,9 @@ def _step(carry, t, predict=True):
217217
ll += log_likelihood
218218

219219
# Predict the next state
220-
if predict:
221-
Q = _get_params(params.dynamics_covariance, 2, t + 1)
222-
u_next = inputs[t + 1] if not predict_final else u
223-
pred_mean, pred_cov, _ = _predict(filtered_mean, filtered_cov, f, Q, lamb, w_mean, w_cov, u_next)
220+
t_next = jnp.minimum(t + 1, num_timesteps - 1)
221+
Q = _get_params(params.dynamics_covariance, 2, t_next)
222+
pred_mean, pred_cov, _ = _predict(filtered_mean, filtered_cov, f, Q, lamb, w_mean, w_cov, inputs[t_next])
224223

225224
# Build carry and output states
226225
carry = (ll, pred_mean, pred_cov)
@@ -237,9 +236,7 @@ def _step(carry, t, predict=True):
237236

238237
# Run the Unscented Kalman Filter
239238
carry = (0.0, params.initial_mean, params.initial_covariance)
240-
carry, outputs = lax.scan(_step, carry, jnp.arange(num_timesteps - 1))
241-
(ll, _, _), final_outputs = _step(carry, num_timesteps - 1, predict=predict_final)
242-
outputs = {key: jnp.concatenate((val, final_outputs[key][None])) for key, val in outputs.items()}
239+
(ll, _, _), outputs = lax.scan(_step, carry, jnp.arange(num_timesteps))
243240
if not predict_final:
244241
outputs = {key: val[:-1] if key.startswith("predicted") else val for key, val in outputs.items()}
245242
outputs = {"marginal_loglik": ll, **outputs}

0 commit comments

Comments
 (0)