Skip to content
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

Transition and measurement models for road networks #812

Open
wants to merge 25 commits into
base: main
Choose a base branch
from

Conversation

sglvladi
Copy link
Collaborator

@sglvladi sglvladi commented Jun 2, 2023

This PR aims to add the following:

  • RoadNetwork type:
    • A subclass of networkx.DiGraph, with some limitations on the allowed node and edge attributes, and some additional methods.
  • OptimalPathToDestinationTransitionModel transition model:
    • A transition model that models a target travelling along the optimal path to a destination, on a given road network.
  • OptimalPathToDestinationMeasurementModel measurement model:
    • A measurement model that projects the target's position on the road network to a 2D position.

TODO

  • Add tests
  • Remove/Reformat example

@sglvladi sglvladi requested a review from a team as a code owner June 2, 2023 13:44
Copy link

codecov bot commented Mar 18, 2024

Codecov Report

Attention: Patch coverage is 96.17647% with 13 lines in your changes are missing coverage. Please review.

Project coverage is 93.56%. Comparing base (7fce938) to head (0f13262).
Report is 23 commits behind head on main.

Files Patch % Lines
stonesoup/models/transition/graph.py 90.10% 4 Missing and 5 partials ⚠️
stonesoup/models/measurement/graph.py 92.68% 0 Missing and 3 partials ⚠️
stonesoup/functions/graph.py 98.03% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #812      +/-   ##
==========================================
+ Coverage   93.49%   93.56%   +0.06%     
==========================================
  Files         200      204       +4     
  Lines       12715    13055     +340     
  Branches     2615     2685      +70     
==========================================
+ Hits        11888    12215     +327     
- Misses        585      590       +5     
- Partials      242      250       +8     
Flag Coverage Δ
integration 65.26% <22.05%> (-1.16%) ⬇️
unittests 89.26% <96.17%> (+0.22%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@sglvladi
Copy link
Collaborator Author

@jswright-dstl, @sdhiscocks this PR is ready for review. I've left the example in for now to help with the review, but plan to remove it before merging.

x_k = \left[r_k, \cdots, e_k, d_k, s_k\right]

and :math:`e_k` denotes the edge the target is currently on, :math:`r_k` is the distance
travelled along the edge, :m ath:`d_k` is the destination node, and :math:`s_k` is
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
travelled along the edge, :m ath:`d_k` is the destination node, and :math:`s_k` is
travelled along the edge, :math:`d_k` is the destination node, and :math:`s_k` is

Comment on lines 100 to 122
v_dest = dict()
for edge in unique_edges:
# Filter paths that contain the edge
filtered_paths = filter(lambda x: np.any(x[1] == edge), s_paths.items())
v_dest_tmp = {dest for (_, dest), _ in filtered_paths}
if len(v_dest_tmp):
try:
v_dest[edge] |= v_dest_tmp
except KeyError:
v_dest[edge] = v_dest_tmp

# Perform destination sampling
resample_inds = np.flatnonzero(
np.random.binomial(1, float(self.destination_resample_probability), num_particles)
)
for i in resample_inds:
try:
v_dest_tmp = list(v_dest[edges[i]])
except KeyError:
# If no valid destinations exist for the current edge, keep the current
# destination
continue
new_state_vectors[-2, i] = np.random.choice(v_dest_tmp)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this reads a little better vs. temp variables. Also change np.random.choice to self.random_state.choice.

Suggested change
v_dest = dict()
for edge in unique_edges:
# Filter paths that contain the edge
filtered_paths = filter(lambda x: np.any(x[1] == edge), s_paths.items())
v_dest_tmp = {dest for (_, dest), _ in filtered_paths}
if len(v_dest_tmp):
try:
v_dest[edge] |= v_dest_tmp
except KeyError:
v_dest[edge] = v_dest_tmp
# Perform destination sampling
resample_inds = np.flatnonzero(
np.random.binomial(1, float(self.destination_resample_probability), num_particles)
)
for i in resample_inds:
try:
v_dest_tmp = list(v_dest[edges[i]])
except KeyError:
# If no valid destinations exist for the current edge, keep the current
# destination
continue
new_state_vectors[-2, i] = np.random.choice(v_dest_tmp)
v_dest = defaultdict(set)
for edge in unique_edges:
# Filter paths that contain the edge
filtered_paths = filter(lambda x: np.any(x[1] == edge), s_paths.items())
v_dest[edge] |= {dest for (_, dest), _ in filtered_paths}
# Perform destination sampling
resample_inds = np.flatnonzero(
np.random.binomial(1, float(self.destination_resample_probability), num_particles)
)
for i in resample_inds:
dests = v_dest[edges[i]]
# If no valid destinations exist for the current edge, keep the current
# destination
if dests:
new_state_vectors[-2, i] = self.random_state.choice(list(dests))

setup.cfg Outdated
Comment on lines 30 to 32
geopandas
shapely
networkx
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wondering if this should be optional

Copy link
Collaborator Author

@sglvladi sglvladi May 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have added these under a new "roadnet" optional deps group. However, now the road net tests are getting skipped. Should I add also these to the "dev" dependencies, or should I modify the CircleCI config to install with "roadnet" deps?


# Perform destination sampling
resample_inds = np.flatnonzero(
np.random.binomial(1, float(self.destination_resample_probability), num_particles)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
np.random.binomial(1, float(self.destination_resample_probability), num_particles)
self.random_state.binomial(1, float(self.destination_resample_probability), num_particles)


def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.random_state = np.random.RandomState(self.seed) if self.seed is not None else None
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
self.random_state = np.random.RandomState(self.seed) if self.seed is not None else None
if self.seed is not None:
self.random_state = np.random.RandomState(self.seed)
else:
self.random_state = np.random.mtrand._rand

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants