-
Notifications
You must be signed in to change notification settings - Fork 141
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adaptive kernel Kalman filter (AKKF) #1014
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mainly minor comments and suggestions for equations.
Few queries mixed in there.
Haven't looked at tutorial yet.
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #1014 +/- ##
==========================================
+ Coverage 93.60% 93.63% +0.03%
==========================================
Files 202 205 +3
Lines 12990 13123 +133
Branches 2651 2668 +17
==========================================
+ Hits 12159 12288 +129
- Misses 588 590 +2
- Partials 243 245 +2
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor doc suggestions, and change to which variable being used to reset caching.
Also found issue with weights of state being inconsistent, so proposed below to fix that.
diff --git a/stonesoup/predictor/kernel.py b/stonesoup/predictor/kernel.py
index e329809d..cdcd1361 100644
--- a/stonesoup/predictor/kernel.py
+++ b/stonesoup/predictor/kernel.py
@@ -76,7 +76,7 @@ class AdaptiveKernelKalmanPredictor(KalmanPredictor):
prediction_covariance = kernel_t @ prior.kernel_covar @ kernel_t.T + v
return Prediction.from_state(prior,
state_vector=new_state_vector,
- weight=np.squeeze(prediction_weights),
+ weight=prediction_weights,
kernel_covar=prediction_covariance,
timestamp=timestamp,
transition_model=self.transition_model)
diff --git a/stonesoup/types/state.py b/stonesoup/types/state.py
index f1965f1b..ea116e7f 100644
--- a/stonesoup/types/state.py
+++ b/stonesoup/types/state.py
@@ -945,7 +945,7 @@ class KernelParticleState(State):
@clearable_cached_property('state_vector', 'weight')
def mean(self):
- return self.state_vector @ self.weight
+ return self.state_vector @ self.weight[:, np.newaxis]
@clearable_cached_property('state_vector', 'weight')
def covar(self):
diff --git a/stonesoup/types/tests/test_state.py b/stonesoup/types/tests/test_state.py
index 3fd80f69..eba966ce 100644
--- a/stonesoup/types/tests/test_state.py
+++ b/stonesoup/types/tests/test_state.py
@@ -863,5 +863,5 @@ def test_kernel_particle_state():
assert np.array_equal(prior.kernel_covar, prior_w_kernel_covar.kernel_covar)
assert number_particles == len(prior)
assert 4 == prior.ndim
- assert np.array_equal(state_vector @ weights, prior.mean)
+ assert np.array_equal(state_vector @ weights[:, np.newaxis], prior.mean)
assert np.array_equal(state_vector @ np.diag(weights) @ state_vector.T, prior.covar)
diff --git a/stonesoup/updater/kernel.py b/stonesoup/updater/kernel.py
index 9860675a..e9998377 100644
--- a/stonesoup/updater/kernel.py
+++ b/stonesoup/updater/kernel.py
@@ -86,14 +86,13 @@ class AdaptiveKernelKalmanUpdater(Updater):
predicted_state.kernel_covar \
@ np.linalg.pinv(G_yy @ predicted_state.kernel_covar
+ self.lambda_updater * np.identity(len(predicted_state)))
- updated_weights = \
- np.atleast_2d(predicted_state.weight).T \
- + Q_AKKF @ (g_y - np.atleast_2d(G_yy @ predicted_state.weight).T)
+ weights = predicted_state.weight[:, np.newaxis]
+ updated_weights = (weights + Q_AKKF@(g_y - G_yy@weights)).ravel()
updated_covariance = \
predicted_state.kernel_covar - Q_AKKF @ G_yy @ predicted_state.kernel_covar
# Proposal Calculation
- pred_mean = predicted_state.state_vector @ np.squeeze(updated_weights)
+ pred_mean = predicted_state.state_vector @ updated_weights
pred_covar = np.diag(np.diag(
predicted_state.state_vector @ updated_covariance @ predicted_state.state_vector.T))
diff --git a/stonesoup/updater/tests/test_kernel.py b/stonesoup/updater/tests/test_kernel.py
index ceea0c64..06c671f7 100644
--- a/stonesoup/updater/tests/test_kernel.py
+++ b/stonesoup/updater/tests/test_kernel.py
@@ -108,7 +108,7 @@ def test_kernel_updater(kernel, measurement_model, c, ialpha):
assert update.hypothesis.measurement.timestamp == gt_state.timestamp
assert np.allclose(update.state_vector, prediction.state_vector)
assert np.allclose(update.proposal, StateVectors(new_state_vector.T), atol=1e0)
- assert np.allclose(update.weight, updated_weights)
+ assert np.allclose(update.weight, updated_weights.ravel())
assert np.allclose(update.kernel_covar, updated_covariance)
This PR adds the Adaptive kernel Kalman filter (AKKF) [1]. The implementation includes the following:
QuadraticKernel
QuarticKernel
GaussianKernel
AdaptiveKernelKalmanPredictor
AdaptiveKernelKalmanUpdater
KernelParticleState
The above implementations are described in [2].
[1] M. Sun, M. E. Davies, I. Proudler and J. R. Hopgood, "Adaptive Kernel Kalman Filter," 2021 Sensor Signal Processing for Defence Conference (SSPD), Edinburgh, United Kingdom, 2021, pp. 1-5, doi: 10.1109/SSPD51364.2021.9541455.
[2] J. S. Wright, J. R. Hopgood, M. E. Davies, I. K. Proudler and M. Sun, "Implementation of Adaptive Kernel Kalman Filter in Stone Soup," 2023 Sensor Signal Processing for Defence Conference (SSPD), Edinburgh, United Kingdom, 2023, pp. 1-5, doi: 10.1109/SSPD57945.2023.10256739.