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

Information and Network Architectures #903

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

Conversation

orosoman-dstl
Copy link
Contributor

@orosoman-dstl orosoman-dstl commented Dec 11, 2023

Introducing the long-awaited architecture module! Thus far, multi-sensor tracking in Stone Soup has operated on the assumption that data from multiple sensors can be fused together seamlessly, but this addition allows experimentation with different architectures.

  • A network architecture models how data is passed around between nodes.
  • An information architecture is models how data is fused together within a network. The information architecture is therefore dependent on the network architecture to operate, as without it there would be no data to fuse.

As there are a fair chunk of new components, we've provided documentation in the form of three tutorials, which I'd recommend starting with to aid your understanding.

orosoman-dstl and others added 30 commits November 29, 2023 08:59
doc="An Edges object containing all edges. For A to be connected to B we would have an "
"Edge with edge_pair=(A, B) in this object.")
current_time: datetime = Property(
default=datetime.now(),
Copy link
Contributor

Choose a reason for hiding this comment

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

If current_time is None this is set to datetime.now() in the __init__ method, but the default value is still datetime.now(). Is this correct?

for leaf_node in self.leaf_nodes:
try:
shortest_path = self.shortest_path_dict[leaf_node][node]
if node != leaf_node or shortest_path != 0:
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a scenario where one of node != leaf_node or shortest_path != 0 is true but not the other?

else:
return 1
except KeyError:
non_leaves += 1
Copy link
Contributor

Choose a reason for hiding this comment

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

non_leaves is unused

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

if any([isinstance(node, RepeaterNode) for node in self.all_nodes]):
Copy link
Contributor

Choose a reason for hiding this comment

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

Does "if len(self.repeater_nodes) != 0:" achieve the same result?

self.information_arch = InformationArchitecture(
edges=self.information_architecture_edges, current_time=self.current_time)
else:
self.information_arch = InformationArchitecture(self.edges, self.current_time)
Copy link
Contributor

Choose a reason for hiding this comment

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

should self.information_architecture_edges be set to self.edges?

Comment on lines +555 to +559
if edge.recipient not in self.information_arch.all_nodes:
edge.update_messages(self.current_time, to_network_node=True,
use_arrival_time=self.use_arrival_time)
else:
edge.update_messages(self.current_time, use_arrival_time=self.use_arrival_time)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
if edge.recipient not in self.information_arch.all_nodes:
edge.update_messages(self.current_time, to_network_node=True,
use_arrival_time=self.use_arrival_time)
else:
edge.update_messages(self.current_time, use_arrival_time=self.use_arrival_time)
edge.update_messages(self.current_time, use_arrival_time=self.use_arrival_time,
to_network_node=(edge.recipient not in self.information_arch.all_nodes))

Comment on lines +571 to +575
if edge.recipient not in self.information_arch.all_nodes:
edge.update_messages(self.current_time, to_network_node=True,
use_arrival_time=self.use_arrival_time)
else:
edge.update_messages(self.current_time, use_arrival_time=self.use_arrival_time)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
if edge.recipient not in self.information_arch.all_nodes:
edge.update_messages(self.current_time, to_network_node=True,
use_arrival_time=self.use_arrival_time)
else:
edge.update_messages(self.current_time, use_arrival_time=self.use_arrival_time)
edge.update_messages(self.current_time, use_arrival_time=self.use_arrival_time,
to_network_node=(edge.recipient not in self.information_arch.all_nodes))

Comment on lines +160 to +167
try:
shortest_path = self.shortest_path_dict[leaf_node][node]
if node != leaf_node or shortest_path != 0:
node_leaves.add(leaf_node)
else:
return 1
except KeyError:
non_leaves += 1
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
try:
shortest_path = self.shortest_path_dict[leaf_node][node]
if node != leaf_node or shortest_path != 0:
node_leaves.add(leaf_node)
else:
return 1
except KeyError:
non_leaves += 1
try:
shortest_path = self.shortest_path_dict[leaf_node][node]
except KeyError:
non_leaves += 1
else:
if node != leaf_node or shortest_path != 0:
node_leaves.add(leaf_node)
else:
return 1

An else clause should be used to move code that isn't expected to cause an exception out of the try-except

metrics: Collection[Collection[MetricGenerator]] = Property(doc="Set of metrics to put in the "
"table")

metrics_labels: Collection[str] = Property(doc='List of titles for ',
Copy link
Contributor

Choose a reason for hiding this comment

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

Sentence is unfinished


class SIAPDiffTableGenerator(Base):
"""
Given two sets of metric generators, the SiapDiffTableGenerator returns a table displaying the
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a reason to specify two sets?

@@ -135,3 +135,112 @@ def set_default_descriptions(self):
"SIAP ID Correctness": "Fraction of true objects with correct ID assignment",
"SIAP ID Ambiguity": "Fraction of true objects with ambiguous ID assignment"
}


class SIAPDiffTableGenerator(Base):
Copy link
Contributor

Choose a reason for hiding this comment

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

Would it be possible for this to inherit from SIAPTableGenerator to save from having to redefine set_default_descriptions and set_default_targets

Comment on lines +49 to +51
def update(self, time_pertaining, time_arrived, data_piece, category, track=None,
use_arrival_time=False):
"""Updates this :class:`~.Node`'s :attr:`~.data_held` using a new data piece. """
Copy link
Contributor

Choose a reason for hiding this comment

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

Add typings to docs

Copy link
Contributor

Choose a reason for hiding this comment

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

Is this or the other two pngs still being used?

Comment on lines +59 to +62
n_archs: int = Property(
doc="Tuple containing a minimum and maximum value for the number of routes created in the "
"network architecture to represent a single edge in the information architecture.",
default=2)
Copy link
Contributor

Choose a reason for hiding this comment

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

Docstring seems inaccurate

# Method 3: Identical Information and Network Architectures
net_arch3 = NetworkArchitecture(edges=info_edges)
assert net_arch3.information_arch.edges == info_edges
assert net_arch3.edges == info_edges
Copy link
Contributor

Choose a reason for hiding this comment

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

possibly add "method 4: Providing only network architecture edges" to generate the info edges

Comment on lines +124 to +125
assert (nodes['a'], nodes['b']) in edges_list.edge_list
assert (nodes['a'], nodes['b']) in edges_list.edge_list
Copy link
Contributor

Choose a reason for hiding this comment

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

Repeated line?

# Check node types
assert len(arch.fusion_nodes) == sum([2, 1])
assert len(arch.sensor_nodes) == sum([2, 2])

Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
assert arch.is_hierarchical

It would make sense to add a check that the "hierarchical" type generates a hierarchical architecture


# Test invalid time inputs
with pytest.raises(TypeError):
A.update(dt0, dt0, 'faux DataPiece', 'created')
Copy link
Contributor

Choose a reason for hiding this comment

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

this test should included a valid data piece to show that its the time causing the fail

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

Successfully merging this pull request may close these issues.

5 participants