Monty. Frameworks. Models. FeatureLocationMatching. FeatureGraphMemory
(ExTbpMonty.Core v0.1.0)
Copy Markdown
View Source
Monty's FeatureGraphMemory: the same library, read by feature.
The feature-at-location module matches an observation in two questions. Which
nodes could I be standing on — that is this module's match_all_node_features
— and, once a pose is being tested, does the pose agree. The first question is
what narrows a graph of twenty thousand nodes to the handful worth walking
from, so it is asked of the whole graph at once.
Why this is a module of functions and not a second struct
Monty spells the difference as a subclass, and the difference is four
functions: hypotheses with poses in them, a node's frame, the matching nodes,
and the comparison behind them. Everything else — the models, the feature rows,
the learning — is GraphMemory's, and a second struct would be the same fields
with a different name. So new/1 builds a GraphMemory and these functions
read it, which is the same shape Monty.Frameworks.Models.GraphMatching.GraphMemory
already has for the evidence module's EvidenceGraphMemory.
The comparison
Monty builds two arrays per column — the observed value plus and minus its
tolerance — and asks numpy whether each node's value lies between them. A
feature that is circular (hue, the first of hsv's three) wraps through 1,
so a tolerance of 0.1 around 0.05 covers [0.95, 1] and [0, 0.15] and the
test becomes "outside" rather than "inside". A node with no stored value has no
evidence for that column and does not match, which is what Monty's NaN
pre-fill does.
Where the tolerances come from
Monty's memory holds features_to_use and the tolerances, because in Python
the module assigns its own tolerances to it. This port's memory holds the
feature names it keeps rows for, which is what the evidence kernel reads,
so the tolerances are passed in by the learning module that owns them — the
same numbers, in the same place as the config that stated them.
Summary
Functions
Monty's FeatureGraphMemory.get_initial_hypotheses.
Monty's get_nodes_with_matching_features: the nodes worth walking from.
Monty's get_rotation_features_at_node: a node's frame, as three directions.
Monty's _match_all_node_features: which nodes could be where I am.
A memory that can answer which nodes have which features.
Functions
@spec get_initial_hypotheses(Monty.Frameworks.Models.GraphMatching.GraphMemory.t()) :: {%{ optional(Monty.Frameworks.Models.GraphMatching.GraphMemory.graph_id()) => map() }, %{ optional(Monty.Frameworks.Models.GraphMatching.GraphMemory.graph_id()) => [ [[float()]] ] }, %{ optional(Monty.Frameworks.Models.GraphMatching.GraphMemory.graph_id()) => [] }}
Monty's FeatureGraphMemory.get_initial_hypotheses.
Three answers, not two: every object is possible, every node of it is a place to have started — as a path of one location, because that is what the next step extends — and no pose is ruled out yet.
@spec get_nodes_with_matching_features( Monty.Frameworks.Models.GraphMatching.GraphMemory.t(), Monty.Frameworks.Models.GraphMatching.GraphMemory.graph_id(), map(), map(), keyword() ) :: {[non_neg_integer()], [[float()]] | [[[float()]]]}
Monty's get_nodes_with_matching_features: the nodes worth walking from.
:list_of_lists wraps every location in a list of its own, which is how Monty
hands back a path that starts on a node — the first observation has no
displacement behind it, so a path is one location.
Only the first input channel is matched: this module is not used in a heterarchy, and Monty says the same in the same place.
@spec get_rotation_features_at_node( Monty.Frameworks.Models.GraphMatching.GraphMemory.t(), Monty.Frameworks.Models.GraphMatching.GraphMemory.graph_id(), non_neg_integer(), Monty.Frameworks.Models.GraphMatching.GraphMemory.input_channel() ) :: [[float()]]
Monty's get_rotation_features_at_node: a node's frame, as three directions.
The stored pose vectors of one node, shaped as the surface normal and the two
principal curvature directions — what align_orthonormal_vectors/2 expects.
@spec match_all_node_features( Monty.Frameworks.Models.GraphMatching.GraphMemory.t(), map(), Monty.Frameworks.Models.GraphMatching.GraphMemory.input_channel(), Monty.Frameworks.Models.GraphMatching.GraphMemory.graph_id(), map() ) :: [non_neg_integer()]
Monty's _match_all_node_features: which nodes could be where I am.
The answer is the indices of the nodes whose every stored feature lies within
the observed value's tolerance — the row layout and its column names being the
memory's own (feature_order), which is why the observation is walked in that
order rather than in the order its features arrive.
A graph being matched always has rows: they are built when the episode starts,
which is what FeatureGraphLM.init_from_ltm/1 does before the first step.
@spec new(keyword()) :: Monty.Frameworks.Models.GraphMatching.GraphMemory.t()
A memory that can answer which nodes have which features.
Monty's FeatureGraphMemory.__init__ takes only the thresholds that decide
when two observations are the same node; the memory it returns is the base
one, because there is nothing else to store.