-
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
Add SMC-PHD components #798
Merged
Merged
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
4f3800a
Add SMCPHDPredictor
sglvladi 50e0b09
Add SMCPHDUpdater
sglvladi e879550
Add SimpleHypothesiser
sglvladi d50d76d
Documentation updates
sglvladi e0c73d4
Refactor resampling code in SMCPHDUpdater
sglvladi b7c3139
Ensure constraint_func is implemented in SMCPHDUpdater
sglvladi 7725508
Update the SMCPHDPredictor to use similar properties to BernouliParti…
sglvladi f173b07
Minor refactoring/tidy up of SMCPHDUpdater
sglvladi 2f5c6ef
Fix SimpleHypothesiser tests coverage and minor change in timestamps …
sglvladi 346cf91
Add SMCPHDBirthSchemeEnum and improve SMCPHDPredictor docs
sglvladi 27ecd5b
Fix typos and improve documentation of SMCPHDUpdater
sglvladi File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
import datetime | ||
|
||
from typing import Set | ||
|
||
from stonesoup.base import Property | ||
from stonesoup.hypothesiser import Hypothesiser | ||
from stonesoup.predictor import Predictor | ||
from stonesoup.types.detection import MissedDetection, Detection | ||
from stonesoup.types.hypothesis import SingleHypothesis | ||
from stonesoup.types.multihypothesis import MultipleHypothesis | ||
from stonesoup.types.track import Track | ||
from stonesoup.updater import Updater | ||
|
||
|
||
class SimpleHypothesiser(Hypothesiser): | ||
"""Simple Hypothesiser class | ||
|
||
Generate track predictions at detection times and create hypotheses for | ||
each detection, as well as a missed detection hypothesis. | ||
""" | ||
predictor: Predictor = Property(doc="Predict tracks to detection times") | ||
updater: Updater = Property( | ||
default=None, | ||
doc="Updater used to get measurement prediction. Only required if " | ||
"`predict_measurement` is `True`. Default is `None`") | ||
check_timestamp: bool = Property( | ||
default=True, | ||
doc="Check that all detections have the same timestamp. Default is `True`") | ||
predict_measurement: bool = Property( | ||
default=False, | ||
doc="Predict measurement for each detection. Default is `True`") | ||
|
||
def __init__(self, *args, **kwargs): | ||
super().__init__(*args, **kwargs) | ||
if self.predict_measurement and self.updater is None: | ||
raise ValueError("Updater must be provided if `predict_measurement` is `True`") | ||
|
||
def hypothesise(self, track: Track, detections: Set[Detection], timestamp: datetime.datetime, | ||
**kwargs) -> MultipleHypothesis: | ||
""" Evaluate and return all track association hypotheses. | ||
|
||
For a given track and a set of N available detections, return a | ||
MultipleHypothesis object with N+1 detections (first detection is | ||
a 'MissedDetection'). | ||
|
||
Parameters | ||
---------- | ||
track : Track | ||
The track object to hypothesise on | ||
detections : set of :class:`~.Detection` | ||
The available detections | ||
timestamp : datetime.datetime | ||
A timestamp used when evaluating the state and measurement | ||
predictions. Note that if a given detection has a non-empty | ||
timestamp, then prediction will be performed according to | ||
the timestamp of the detection. | ||
|
||
Returns | ||
------- | ||
: :class:`~.MultipleHypothesis` | ||
A container of :class:`~SingleHypothesis` objects | ||
|
||
""" | ||
|
||
if self.check_timestamp: | ||
# Check to make sure all detections are obtained from the same time | ||
timestamps = {detection.timestamp for detection in detections} | ||
if len(timestamps) > 1: | ||
raise ValueError("All detections must have the same timestamp") | ||
|
||
hypotheses = [] | ||
|
||
# Common state prediction | ||
prediction = self.predictor.predict(track, timestamp=timestamp, **kwargs) | ||
|
||
# Missed detection hypothesis | ||
hypotheses.append( | ||
SingleHypothesis(prediction, MissedDetection(timestamp=timestamp)) | ||
) | ||
|
||
# True detection hypotheses | ||
for detection in detections: | ||
|
||
# Re-evaluate prediction | ||
prediction = self.predictor.predict(track, timestamp=detection.timestamp, **kwargs) | ||
|
||
# Compute measurement prediction | ||
if self.predict_measurement: | ||
measurement_prediction = self.updater.predict_measurement( | ||
prediction, timestamp=detection.timestamp, **kwargs) | ||
else: | ||
measurement_prediction = None | ||
|
||
hypotheses.append( | ||
SingleHypothesis(prediction, detection, measurement_prediction) | ||
) | ||
|
||
return MultipleHypothesis(hypotheses) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import datetime | ||
import numpy as np | ||
import pytest | ||
|
||
from stonesoup.hypothesiser.simple import SimpleHypothesiser | ||
from stonesoup.types.detection import Detection | ||
from stonesoup.types.state import GaussianState | ||
from stonesoup.types.track import Track | ||
|
||
|
||
@pytest.mark.parametrize( | ||
"check_timestamp, predict_measurement", | ||
[ | ||
(True, False), | ||
(False, True), | ||
(False, False) | ||
] | ||
) | ||
def test_simple(predictor, updater, check_timestamp, predict_measurement): | ||
timestamp = datetime.datetime.now() | ||
track = Track([GaussianState(np.array([[0]]), np.array([[1]]), timestamp)]) | ||
|
||
# Create 3 detections, 2 of which are at the same time | ||
detection1 = Detection(np.array([[2]]), timestamp) | ||
detection2 = Detection(np.array([[3]]), timestamp + datetime.timedelta(seconds=1)) | ||
detection3 = Detection(np.array([[10]]), timestamp + datetime.timedelta(seconds=1)) | ||
detections = {detection1, detection2, detection3} | ||
|
||
hypothesiser = SimpleHypothesiser(predictor, updater, check_timestamp, predict_measurement) | ||
|
||
if check_timestamp: | ||
# Detection 1 has different timestamp to Detections 2 and 3, so this should raise an error | ||
with pytest.raises(ValueError): | ||
hypothesiser.hypothesise(track, detections, timestamp) | ||
return | ||
|
||
hypotheses = hypothesiser.hypothesise(track, detections, timestamp) | ||
|
||
# There are 3 hypotheses - Detection 1, Detection 2, Detection 3, Missed Detection | ||
assert len(hypotheses) == 4 | ||
|
||
# There is a missed detection hypothesis | ||
assert any(not hypothesis.measurement for hypothesis in hypotheses) | ||
|
||
if predict_measurement: | ||
# Each true hypothesis has a measurement prediction | ||
true_hypotheses = [hypothesis for hypothesis in hypotheses if hypothesis] | ||
assert all(hypothesis.measurement_prediction is not None for hypothesis in true_hypotheses) | ||
else: | ||
assert all(hypothesis.measurement_prediction is None for hypothesis in hypotheses) | ||
|
||
|
||
def test_invalid_simple_arguments(predictor): | ||
with pytest.raises(ValueError): | ||
SimpleHypothesiser(predictor, updater=None, predict_measurement=True) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Codecov reported as not covered by test, which seems odd when test below should be setting
predict_measurement
to True and False.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.
Nice spot. I have added an extra test case to make sure we reach that part of the code.