Iterated Posterior Linearization Smoother (IPLS)#1029
Iterated Posterior Linearization Smoother (IPLS)#1029
Conversation
Co-authored-by: Steven Hiscocks <sdhiscocks@dstl.gov.uk>
# Conflicts: # stonesoup/functions/tests/test_functions.py
csherman-dstl
left a comment
There was a problem hiding this comment.
There are a number of places where changes have been made to parts of the codebase that are not parts of this PR. Otherwise can I confirm that all changes that have been made in #1016 are included within this PR?
| def matrix(self, **kwargs): return self.meas_matrix | ||
|
|
||
| def bias(self, **kwargs): return self.bias_value |
There was a problem hiding this comment.
put returns on different lines
| from stonesoup.types.array import Matrix | ||
| from stonesoup.types.state import StateVector |
There was a problem hiding this comment.
Imports should be at the top of the file
|
|
||
| return self.noise_covar | ||
|
|
||
| class GeneralLinearGaussian(MeasurementModel, LinearModel, GaussianModel): |
There was a problem hiding this comment.
This class can inherit most methods from LinearGaussian
| self.noise_covar = CovarianceMatrix(self.noise_covar) | ||
|
|
||
| @property | ||
| def ndim_state(self): |
There was a problem hiding this comment.
Identical to inherited method
|
|
||
| from ..linear import KnownTurnRateSandwich | ||
| from ..linear import ConstantVelocity | ||
| from ..linear import LinearTransitionModel |
stonesoup/types/prediction.py
Outdated
| MeasurementPrediction.register(CompositeState) # noqa: E305 | ||
|
|
||
|
|
||
| class AugmentedGaussianState(GaussianState): |
There was a problem hiding this comment.
This class belongs in stonesoup.types.state
stonesoup/predictor/kalman.py
Outdated
| transition_model: TransitionModel = Property(doc="The transition model to be used.") | ||
| control_model: ControlModel = Property( | ||
| default=None, | ||
| doc="The control model to be used. Default `None` where the predictor " | ||
| "will create a zero-effect linear :class:`~.ControlModel`.") | ||
| alpha: float = Property( | ||
| default=0.5, | ||
| doc="Primary sigma point spread scaling parameter. Default is 0.5.") | ||
| beta: float = Property( | ||
| default=2, | ||
| doc="Used to incorporate prior knowledge of the distribution. If the " | ||
| "true distribution is Gaussian, the value of 2 is optimal. " | ||
| "Default is 2") | ||
| kappa: float = Property( | ||
| default=0, | ||
| doc="Secondary spread scaling parameter. Default is calculated as " | ||
| "3-Ns") |
There was a problem hiding this comment.
All properties are inherited from UnscentedKalmanPredictor
stonesoup/smoother/kalman.py
Outdated
| transition_model: TransitionModel = Property(doc="The transition model to be used.") | ||
| measurement_model: MeasurementModel = Property(default=None, doc="The measurement model to be used.") | ||
| alpha: float = Property( | ||
| default=0.5, | ||
| doc="Primary sigma point spread scaling parameter. Default is 0.5.") | ||
| beta: float = Property( | ||
| default=2, | ||
| doc="Used to incorporate prior knowledge of the distribution. If the " | ||
| "true distribution is Gaussian, the value of 2 is optimal. " | ||
| "Default is 2") | ||
| kappa: float = Property( | ||
| default=0, | ||
| doc="Secondary spread scaling parameter. Default is calculated as " | ||
| "3-Ns") |
There was a problem hiding this comment.
Multiple properties are inherited from UnscentedKalamanSmoother
stonesoup/smoother/kalman.py
Outdated
| while True: | ||
| # we have no test of convergence, but limited the number of iterations | ||
| if len(smoothed_tracks) >= self.n_iterations: | ||
| # warnings.warn("IPLS reached pre-specified number of iterations.") | ||
| break |
There was a problem hiding this comment.
Does it make more sense for this to just be a for loop?
|
@csherman-dstl, I’ve checked, and this PR does include the changes from #1016. I’ve applied the corrections you suggested and pushed the updates. |
| model = LinearTransitionModel( | ||
| transition_matrix=F, | ||
| bias_value=a_vector, | ||
| noise_covar=Q |
There was a problem hiding this comment.
| noise_covar=Q | |
| covariance_matrix=Q |
The noise_covar property has been renamed to covariance_matrix but there are number of occurrences in the tests where this hasn't been changed.
| ), | ||
| ( # Augmented Kalman | ||
| AugmentedKalmanPredictor, | ||
| LinearTransitionModel( | ||
| transition_matrix=np.array([[0.8, 0.2], [0.3, 0.7]]), | ||
| bias_value=np.zeros([2, 1]), | ||
| noise_covar=np.diag([0.10961003, 0.88557178]) | ||
| ), | ||
| ( # Augmented Unscented Kalman | ||
| AugmentedUnscentedKalmanPredictor, | ||
| LinearTransitionModel( | ||
| transition_matrix=np.array([[0.8, 0.2], [0.3, 0.7]]), | ||
| bias_value=np.zeros([2, 1]), | ||
| noise_covar=np.diag([0.10961003, 0.88557178]) | ||
| ), | ||
|
|
||
| ], | ||
| ids=["standard", "extended", "unscented", "cubature", "stochasticIntegration"] | ||
| ) | ||
|
|
||
| ids=["standard", "extended", "unscented", "cubature", "stochasticIntegration", "augmented_kalman", "augmented_unscented_kalman"] | ||
|
|
There was a problem hiding this comment.
There's a mismatch in the number of brackets used here and also there are no given values for prior_mean and prior_covar
stonesoup/smoother/kalman.py
Outdated
| continue | ||
|
|
||
| """ Compute SLR parameters (transition). """ | ||
| from stonesoup.types.prediction import Prediction |
There was a problem hiding this comment.
| from stonesoup.types.prediction import Prediction |
Prediction is already imported in this file
stonesoup/smoother/kalman.py
Outdated
| ) | ||
| f_matrix, a_vector, lambda_cov_matrix = slr_definition(previous_state, trans_fun, force_symmetry=True) | ||
|
|
||
| "Perform linear time update" |
There was a problem hiding this comment.
# should be used for comments (this occurs multiple times in this method)
| "Perform linear time update" | |
| # Perform linear time update |
stonesoup/updater/iterated.py
Outdated
| from functools import partial | ||
| from ..types.state import State |
There was a problem hiding this comment.
State is unused and from functools import partial should be near the top of the file
stonesoup/updater/iterated.py
Outdated
| break | ||
|
|
||
| # SLR is wrt to tne approximated posterior in post_state, not the original prior in hypothesis.prediction | ||
| meas_fun = partial(super().predict_measurement, measurement_model=measurement_model, |
There was a problem hiding this comment.
| meas_fun = partial(super().predict_measurement, measurement_model=measurement_model, | |
| meas_fun = partial(self.predict_measurement, measurement_model=measurement_model, |
As far as I can see predict_measurement has not been overwritten and so super() will call the same thing as just using self.
stonesoup/smoother/kalman.py
Outdated
| smoothed_tracks = [] | ||
|
|
||
| for iteration in range(self.n_iterations): | ||
|
|
||
| if iteration == 0: | ||
| # initialising by performing sigma-point smoothing via the UKF smoother | ||
| smoothed_track = UnscentedKalmanSmoother(transition_model=self.transition_model, | ||
| alpha=self.alpha, | ||
| beta=self.beta, | ||
| kappa=self.kappa).smooth(track) | ||
| smoothed_tracks.append(smoothed_track) | ||
| continue | ||
| else: | ||
| smoothed_track = smoothed_tracks[-1] |
There was a problem hiding this comment.
| smoothed_tracks = [] | |
| for iteration in range(self.n_iterations): | |
| if iteration == 0: | |
| # initialising by performing sigma-point smoothing via the UKF smoother | |
| smoothed_track = UnscentedKalmanSmoother(transition_model=self.transition_model, | |
| alpha=self.alpha, | |
| beta=self.beta, | |
| kappa=self.kappa).smooth(track) | |
| smoothed_tracks.append(smoothed_track) | |
| continue | |
| else: | |
| smoothed_track = smoothed_tracks[-1] | |
| smoothed_track = UnscentedKalmanSmoother(transition_model=self.transition_model, | |
| alpha=self.alpha, | |
| beta=self.beta, | |
| kappa=self.kappa).smooth(track) | |
| for _ in range(self.n_iterations): |
I'm not sure of the need for the smoothed_tracks list, as far as I can tell only the latest version is ever used.
Also the smoothed_track can be initiated outside the loop instead of using the first iteration to only do this
|
@csherman-dstl, thank you for your feedback. I've implemented your comments, and pushed the updates. I see that there are a number of test failures (311, 312, 313) following the check, but they appear to be related to |
| Parameters | ||
| ---------- | ||
| prior : :class:`~.State` | ||
| Prior state, :math:`\mathbf{x}_{k-1}` | ||
| timestamp : :class:`datetime.datetime` | ||
| Time to transit to (:math:`k`) | ||
| **kwargs : various, optional | ||
| These are passed to :meth:`~.TransitionModel.covar` | ||
|
|
There was a problem hiding this comment.
should control_input and transition_noise be included in the docstring?
stonesoup/types/state.py
Outdated
|
|
||
|
|
||
| class AugmentedGaussianState(GaussianState): | ||
| """ This is a new GaussianState class that can also store information on cross-covariance |
There was a problem hiding this comment.
| """ This is a new GaussianState class that can also store information on cross-covariance | |
| """ This is a GaussianState class that can also store information on cross-covariance |
The class won't always be new
| assert timestamp == prediction.timestamp | ||
|
|
||
|
|
||
| def test_augmentedgaussianstateprediction(): |
There was a problem hiding this comment.
Should this test check the value of cross_covar
stonesoup/updater/iterated.py
Outdated
| "KLDivergence between current and prior posterior state estimate.") | ||
| max_iterations: int = Property( | ||
| default=5, | ||
| doc="Number of iterations before while loop is exited and a non-convergence warning is " |
There was a problem hiding this comment.
Is this docstring still accurate? As far as I can see the warning is commented out.
|
@csherman-dstl, thanks again for the comments, all implemented now. The documentation build is currently failing due to the CI environment:
As noted above, the other test failures are not related to this pull request. |
This PR adds the following classes:
The Iterated Posterior Linearization Smoother (IPLS) described in [1] performs statistical linear regression (SLR) of the nonlinear transition and measurement models w.r.t. to the current posterior approximation.
A running example can be found here.
[1] Á. F. García-Fernández, L. Svensson and S. Särkkä, "Iterated Posterior Linearization Smoother," in IEEE Transactions on Automatic Control, vol. 62, no. 4, pp. 2056-2063, April 2017, doi: 10.1109/TAC.2016.2592681.