class TestKalmanContinuousDiscrete(OrnsteinUhlenbeckCDTestCase): """ Try Kalman filtering on a continuous-discrete setting. Try OU process. """ def setUp(self): super().setup_ornsteinuhlenbeck() self.method = Kalman(self.dynmod, self.measmod, self.initrv) def test_dynamicmodel(self): self.assertEqual(self.dynmod, self.method.dynamicmodel) def test_measurementmodel(self): self.assertEqual(self.measmod, self.method.measurementmodel) def test_initialdistribution(self): self.assertEqual(self.initrv, self.method.initialrandomvariable) def test_predict_shape(self): pred, _ = self.method.predict(0.0, self.delta_t, self.initrv) self.assertEqual(pred.mean.shape, (1, )) self.assertEqual(pred.cov.shape, (1, 1)) def test_predict_value(self): pred, _ = self.method.predict(0.0, self.delta_t, self.initrv) ah = scipy.linalg.expm(self.delta_t * self.drift) qh = (self.q / (2 * self.lam) * (1 - scipy.linalg.expm(2 * self.drift * self.delta_t))) expectedmean = np.squeeze(ah @ (self.initrv.mean * np.ones(1))) expectedcov = np.squeeze(ah @ (self.initrv.cov * np.eye(1)) @ ah.T + qh) self.assertApproxEqual(expectedmean, pred.mean) self.assertApproxEqual(expectedcov, pred.cov) def test_update(self): data = self.measmod.transition_realization( self.initrv.mean * np.ones(1), 0.0)[0].sample() upd, __, __, __ = self.method.update(0.0, self.initrv, data) self.assertEqual(upd.mean.shape, (1, )) self.assertEqual(upd.cov.shape, (1, 1)) def test_smoother(self): """ RMSE of filter smaller than rmse of measurements? """ filter_posterior = self.method.filter(self.obs, self.tms) filtms = filter_posterior.state_rvs.mean smooth_posterior = self.method.filtsmooth(self.obs, self.tms) smooms = smooth_posterior.state_rvs.mean self.assertEqual(filtms[1:].shape, self.states[1:].shape) self.assertEqual(smooms[1:].shape, self.states[1:].shape) self.assertEqual(self.obs.shape, self.states[1:].shape) normaliser = np.sqrt(self.states[1:].size) filtrmse = np.linalg.norm(filtms[1:] - self.states[1:]) / normaliser smoormse = np.linalg.norm(smooms[1:] - self.states[1:]) / normaliser obs_rmse = np.linalg.norm(self.obs - self.states[1:]) / normaliser if VISUALISE is True: plt.title("Ornstein Uhlenbeck (%.2f < " % smoormse + "%.2f < %.2f?)" % (filtrmse, obs_rmse)) plt.plot(self.tms[1:], self.obs[:, 0], ".", label="Observations", alpha=0.5) plt.plot(self.tms, filtms, "-", label="Filter guess") plt.plot(self.tms, smooms, "-", label="Smoother guess") plt.plot(self.tms, self.states, "-", linewidth=6, alpha=0.25, label="Truth") plt.legend() plt.show() self.assertLess(smoormse, filtrmse) self.assertLess(filtrmse, obs_rmse)
def setUp(self): super().setup_ornsteinuhlenbeck() self.method = Kalman(self.dynmod, self.measmod, self.initrv)
def setUp(self): super().setup_cartracking() self.method = Kalman(self.dynmod, self.measmod, self.initrv)
class TestKalmanDiscreteDiscrete(CarTrackingDDTestCase): """ Try Kalman filtering and smoothing on a discrete setting. """ def setUp(self): super().setup_cartracking() self.method = Kalman(self.dynmod, self.measmod, self.initrv) def test_dynamicmodel(self): self.assertEqual(self.dynmod, self.method.dynamicmodel) def test_measurementmodel(self): self.assertEqual(self.measmod, self.method.measurementmodel) def test_initialdistribution(self): self.assertEqual(self.initrv, self.method.initialrandomvariable) def test_predict(self): pred, _ = self.method.predict(0.0, self.delta_t, self.initrv) self.assertEqual(pred.mean.ndim, 1) self.assertEqual(pred.mean.shape[0], 4) self.assertEqual(pred.cov.ndim, 2) self.assertEqual(pred.cov.shape[0], 4) self.assertEqual(pred.cov.shape[1], 4) def test_update(self): data = self.measmod.transition_realization(self.initrv.mean, 0.0)[0].sample() upd, __, __, __ = self.method.update(0.0, self.initrv, data) self.assertEqual(upd.mean.ndim, 1) self.assertEqual(upd.mean.shape[0], 4) self.assertEqual(upd.cov.ndim, 2) self.assertEqual(upd.cov.shape[0], 4) self.assertEqual(upd.cov.shape[1], 4) def test_filtsmooth(self): """ RMSE of smoother smaller than rmse of filter smaller than of measurements? """ filter_posterior = self.method.filter(self.obs, self.tms) filtms = filter_posterior.state_rvs.mean smooth_posterior = self.method.filtsmooth(self.obs, self.tms) smooms = smooth_posterior.state_rvs.mean normaliser = np.sqrt(self.states[1:, :2].size) filtrmse = np.linalg.norm(filtms[1:, :2] - self.states[1:, :2]) / normaliser smoormse = np.linalg.norm(smooms[1:, :2] - self.states[1:, :2]) / normaliser obs_rmse = np.linalg.norm(self.obs - self.states[1:, :2]) / normaliser if VISUALISE is True: plt.title("Car tracking trajectory (%.2f " % smoormse + "< %.2f < %.2f?)" % (filtrmse, obs_rmse)) plt.plot(self.obs[:, 0], self.obs[:, 1], ".", label="Observations", alpha=0.5) plt.plot(filtms[:, 0], filtms[:, 1], "-", label="Filter guess") plt.plot(smooms[:, 0], smooms[:, 1], "-", label="Smoother guess") plt.plot( self.states[:, 0], self.states[:, 1], "-", linewidth=6, alpha=0.25, label="Truth", ) plt.legend() plt.show() self.assertLess(smoormse, filtrmse) self.assertLess(filtrmse, obs_rmse)
class TestKalmanPosterior(CarTrackingDDTestCase, NumpyAssertions): def setUp(self): super().setup_cartracking() self.method = Kalman(self.dynmod, self.measmod, self.initrv) self.posterior = self.method.filter(self.obs, self.tms) def test_len(self): self.assertTrue(len(self.posterior) > 0) self.assertEqual(len(self.posterior.locations), len(self.posterior)) self.assertEqual(len(self.posterior.state_rvs), len(self.posterior)) def test_locations(self): self.assertArrayEqual(self.posterior.locations, np.sort(self.posterior.locations)) self.assertApproxEqual(self.posterior.locations[0], self.tms[0]) self.assertApproxEqual(self.posterior.locations[-1], self.tms[-1]) def test_getitem(self): self.assertArrayEqual(self.posterior[0].mean, self.posterior.state_rvs[0].mean) self.assertArrayEqual(self.posterior[0].cov, self.posterior.state_rvs[0].cov) self.assertArrayEqual(self.posterior[-1].mean, self.posterior.state_rvs[-1].mean) self.assertArrayEqual(self.posterior[-1].cov, self.posterior.state_rvs[-1].cov) self.assertArrayEqual(self.posterior[:].mean, self.posterior.state_rvs[:].mean) self.assertArrayEqual(self.posterior[:].cov, self.posterior.state_rvs[:].cov) def test_state_rvs(self): self.assertTrue( isinstance(self.posterior.state_rvs, _RandomVariableList)) self.assertEqual(len(self.posterior.state_rvs[0].shape), 1) self.assertEqual(self.posterior.state_rvs[-1].shape, self.initrv.shape) def test_call_error_if_small(self): self.assertLess(-0.5, self.tms[0]) with self.assertRaises(ValueError): self.posterior(-0.5) def test_call_vectorisation(self): locs = np.arange(0, 1, 20) evals = self.posterior(locs) self.assertEqual(len(evals), len(locs)) def test_call_interpolation(self): self.assertLess(self.tms[0], 9.88) self.assertGreater(self.tms[-1], 9.88) self.assertTrue(9.88 not in self.tms) self.posterior(9.88) def test_call_to_discrete(self): self.assertEqual(self.tms[0], 0) self.assertArrayEqual(self.posterior(0.0).mean, self.posterior[0].mean) self.assertArrayEqual(self.posterior(0.0).cov, self.posterior[0].cov) self.assertEqual(self.tms[-1], 19.8) self.assertArrayEqual( self.posterior(19.8).mean, self.posterior[-1].mean) self.assertArrayEqual(self.posterior(19.8).cov, self.posterior[-1].cov) self.assertArrayEqual( self.posterior(self.tms[2]).mean, self.posterior[2].mean) self.assertArrayEqual( self.posterior(self.tms[5]).mean, self.posterior[5].mean) self.assertArrayEqual( self.posterior(self.tms[10]).mean, self.posterior[10].mean) def test_call_extrapolation(self): self.assertGreater(30, self.tms[-1]) self.posterior(30)
def setUp(self): super().setup_cartracking() self.method = Kalman(self.dynmod, self.measmod, self.initrv) self.posterior = self.method.filter(self.obs, self.tms)
class TestKalmanPosteriorSampling(CarTrackingDDTestCase, NumpyAssertions): def setUp(self): super().setup_cartracking() self.method = Kalman(self.dynmod, self.measmod, self.initrv) self.posterior = self.method.filter(self.obs, self.tms) def test_output_shape(self): loc_inputs = [ None, self.posterior.locations[[2, 3]], np.arange(0.0, 0.5, 0.025), ] dim = (self.method.dynamod.dimension, ) single_sample_shapes = [ (len(self.posterior), self.method.dynamod.dimension), (2, self.method.dynamod.dimension), (len(loc_inputs[-1]), self.method.dynamod.dimension), ] for size in [(), (5, ), (2, 3, 4)]: for loc, loc_shape in zip(loc_inputs, single_sample_shapes): with self.subTest(size=size, loc=loc): sample = self.posterior.sample(locations=loc, size=size) if size == (): self.assertEqual(sample.shape, loc_shape) else: self.assertEqual(sample.shape, size + loc_shape) def test_sampling_all_locations_multiple_samples(self): five_samples = self.posterior.sample(size=5) chi_squared = np.array([ chi_squared_statistic(sample, self.posterior[:].mean, self.posterior[:].cov) for sample in five_samples ]).mean() self.assertLess(chi_squared, 10.0) self.assertLess(0.1, chi_squared) def test_sampling_two_locations_multiple_samples(self): locs = self.posterior.locations[[2, 3]] five_samples = self.posterior.sample(locations=locs, size=5) chi_squared = np.array([ chi_squared_statistic( sample, self.posterior[:].mean[[2, 3]], self.posterior[:].cov[[2, 3]], ) for sample in five_samples ]).mean() self.assertLess(chi_squared, 10.0) self.assertLess(0.1, chi_squared) def test_sampling_many_locations_multiple_samples(self): locs = np.arange(0.0, 0.5, 0.025) five_samples = self.posterior.sample(locations=locs, size=5) chi_squared = np.array([ chi_squared_statistic(sample, self.posterior(locs).mean, self.posterior(locs).cov) for sample in five_samples ]).mean() self.assertLess(chi_squared, 10.0) self.assertLess(0.1, chi_squared)