Monty.Frameworks.Models.DisplacementMatching.DisplacementGraphLM (ExTbpMonty.Core v0.1.0)

Copy Markdown View Source

A learning module that recognises objects by the displacements between what it senses: Monty's DisplacementGraphLM.

Every step, the module knows how far the sensor moved. A graph of an object knows which nodes are joined by which displacement, so "did this object's graph have an edge like that, from where I think I am" narrows the object down without comparing a single feature. Two answers at the end: the object, and the turn between the displacements it sensed and the ones its graph predicted — which together with a scale is the object's pose.

This is the simplest learning module in Monty and the one the tutorial starts from: one sensor, one module, one object, a thousand exploratory steps.

What it composes

%Monty.Frameworks.Models.GraphMatching.GraphLM{} is the graph module underneath — the buffer, the goal generator, the votes, the conclusion — and this adds the three things a displacement match needs: the paths being tested, the paths each step extends them to, and the scale factors that make the length comparison relative. Monty inherits those from GraphLM; here they are fields of the class that uses them, and the matching step is written out rather than delegated, because the paths are this class's state and a callback could not hand them back.

Not ported

Nothing of Monty's class is missing: match_attribute: "displacement" compares the step's movement with a stored one, and "PPF" compares four numbers per edge — the length and the three angles between it and the nodes' normals — which is what makes the match independent of how the object is turned. A config naming a third attribute is refused rather than matched differently.

Summary

Functions

Monty's _add_detailed_stats: the paths a match came from.

Monty's _add_displacements for this class: the step's displacement and the point-pair features of the move.

Records whether this step's observation reached the module.

Monty's _reset_modules_buffers: drop the observations, keep the memory.

Forgets what the module concluded, keeping what it believes.

Drops the hypotheses an unsupervised module never earned.

Monty's _compute_possible_matches: what the displacement says is still possible.

What this module concluded, as the model reads it.

Monty's exploratory_step: keep the observation, match nothing yet.

Monty's fixme_update_ground_truth: which objects the graph was built from.

The module a config asks for, as Monty's __init__ reads its arguments.

The turn between two sets of vectors, Monty's get_object_rotation.

How much bigger the object is than the one in memory — Monty's get_object_scale.

Monty's get_output: a displacement module publishes no hypothesis.

Monty's get_unique_pose_if_available: where the object is, once the path is the only one left.

The graph memory this module holds, passed to the next episode.

What the module starts an episode from: every object in memory, every node as a path, every edge as the path the first movement is matched against.

Monty's load_state_dict: a memory, and the module state that follows it.

Monty's matching_step: match this step's displacement against the paths left.

A module with an empty memory.

Whether the last buffered observation was one this module processed.

Monty's propose_goals: what the goal generator last asked for.

Monty's propose_region: the region its goals are in.

Monty's receive_votes: another module ruling an object out.

The same, with the buffer and the paths emptied first — Monty's reset_stm.

Monty's send_out_vote: the objects this module knows this one is not.

Tells the module whether this episode trains or evaluates.

Names this object without emptying the module's history.

Records which object the module's sensor is on this step.

Monty's fixme_reset_ground_truth: the object this episode is about.

Sets the conclusion without asking the module to reconsider.

Monty's state_dict: the memory, and which objects it was built from.

The supervised conclusion an experiment states, in place of matching one.

Monty's update_ltm_from_stm: a training episode's observations, into memory.

Monty's update_stats_after_vote: what a vote round left it believing.

Monty's update_terminal_condition: has this module concluded?

Types

t()

@type t() :: %Monty.Frameworks.Models.DisplacementMatching.DisplacementGraphLM{
  graph_lm: term(),
  match_attribute: term(),
  next_possible_paths: term(),
  possible_paths: term(),
  scale_factors: term(),
  tolerance: term(),
  use_relative_len: term()
}

Functions

add_detailed_stats(lm, stats)

@spec add_detailed_stats(t(), map()) :: map()

Monty's _add_detailed_stats: the paths a match came from.

add_displacements(lm, percepts)

@spec add_displacements(t(), [Monty.Cmp.Message.t()]) ::
  {:ok, [Monty.Cmp.Message.t()]} | {:error, term()}

Monty's _add_displacements for this class: the step's displacement and the point-pair features of the move.

The displacement is what a match is made of; the pair of features is what a point-pair-feature module would match on, and Monty computes both whatever the match attribute is. Both are set on every percept, because a learning module's own output moves with the sensor.

add_lm_processing_to_buffer_stats(lm, processed)

@spec add_lm_processing_to_buffer_stats(t(), boolean()) :: t()

Records whether this step's observation reached the module.

clear_buffers(state)

@spec clear_buffers(t()) :: t()

Monty's _reset_modules_buffers: drop the observations, keep the memory.

clear_conclusion(state)

@spec clear_conclusion(t()) :: t()

Forgets what the module concluded, keeping what it believes.

clear_hypotheses(lm)

@spec clear_hypotheses(t()) :: t()

Drops the hypotheses an unsupervised module never earned.

compute_possible_matches(lm, ctx, observation, bool)

@spec compute_possible_matches(
  t(),
  Monty.Context.t(),
  [Monty.Cmp.Message.t()],
  boolean()
) :: t()

Monty's _compute_possible_matches: what the displacement says is still possible.

This is a prediction, not a comparison of features: for every object still possible, take the paths this episode could be on, extend each along the edges whose displacement matches what was just sensed, and predict whether the sensor is still on the object. An object whose prediction is wrong — because no edge had that displacement, so the sensor cannot be on it — is ruled out.

conclusion(lm)

What this module concluded, as the model reads it.

exploratory_step(percepts, ctx, lm)

@spec exploratory_step([Monty.Cmp.Message.t()], Monty.Context.t(), t()) :: {:ok, t()}

Monty's exploratory_step: keep the observation, match nothing yet.

fixme_update_ground_truth(lm)

@spec fixme_update_ground_truth(t()) :: t()

Monty's fixme_update_ground_truth: which objects the graph was built from.

from_config(config, options \\ [])

@spec from_config(map(), keyword()) :: {:ok, t()} | {:error, term()}

The module a config asks for, as Monty's __init__ reads its arguments.

:memory is the graphs the module starts from — Monty loads a state dict into a module it has just built, which is the same thing one step earlier. A match_attribute this port has not written is an error rather than a different experiment, which is what new/1 says out loud.

get_object_rotation(sensed_displacements, model_displacements, get_reverse_r \\ false)

@spec get_object_rotation([[number()]], [[number()]], boolean()) ::
  {[float()], [[float()]], Monty.Geometry.Rotation.t()}

The turn between two sets of vectors, Monty's get_object_rotation.

{euler degrees, matrix, rotation}: the rotation that takes the sensed displacements onto the modelled ones, or — with get_reverse_r — the one that does the opposite, which turns the model to fit the senses.

Two displacements along one line do not say how far to turn about it, so the answer is one of the rotations that fit; Monty's own comment says to keep moving, and so does this — nothing here refuses a pose.

get_object_scale(sensed_displacement, model_displacement)

@spec get_object_scale([number()], [number()]) :: float()

How much bigger the object is than the one in memory — Monty's get_object_scale.

get_output(state)

@spec get_output(t()) :: {nil, t()}

Monty's get_output: a displacement module publishes no hypothesis.

get_unique_pose_if_available(lm, object_id)

@spec get_unique_pose_if_available(t(), String.t()) :: {:ok, map()} | :not_narrowed

Monty's get_unique_pose_if_available: where the object is, once the path is the only one left.

[x, y, z, euler_x, euler_y, euler_z, scale] — the sensor's place on the object, the turn between the displacements it sensed and the ones the graph predicted, and how much bigger or smaller the object is than the one learned. Nothing until exactly one path is possible: two paths are two places to be.

graph_memory(lm)

@spec graph_memory(t()) ::
  Monty.Frameworks.Models.GraphMatching.GraphMemory.t() | :none

The graph memory this module holds, passed to the next episode.

init_from_ltm(lm)

@spec init_from_ltm(t()) :: t()

What the module starts an episode from: every object in memory, every node as a path, every edge as the path the first movement is matched against.

Monty's init_from_ltm. The memory is what knows the graphs; this holds the paths, because a path is where this episode's sensor has been.

load_state_dict(lm, memento)

@spec load_state_dict(t(), map()) :: t()

Monty's load_state_dict: a memory, and the module state that follows it.

matching_step(percepts, ctx, lm)

@spec matching_step([Monty.Cmp.Message.t()], Monty.Context.t(), t()) :: {:ok, t()}

Monty's matching_step: match this step's displacement against the paths left.

A step that carried only a location is nothing to match; the rest narrow the possible matches, and a module nothing is possible for any more concludes :no_match.

new(options \\ [])

@spec new(keyword()) :: t()

A module with an empty memory.

:k is how many neighbours each node of a graph links to — the edges are what a displacement is matched against, so a graph with no edges can match nothing. :tolerance is how far an observed displacement may sit from a stored one and still be called the same; :use_relative_len divides lengths by the first displacement, which is scale invariance at the cost of a scale you cannot measure. :match_attribute says which of the two comparisons a match makes: "displacement" (the default) or "PPF", whose graphs are built with point-pair features on their edges.

observation_processed?(lm)

@spec observation_processed?(t()) :: boolean()

Whether the last buffered observation was one this module processed.

propose_goals(state)

@spec propose_goals(t()) :: {[Monty.Cmp.Goal.t()], t()}

Monty's propose_goals: what the goal generator last asked for.

propose_region(state)

@spec propose_region(t()) :: {Monty.Cmp.AttentionRegion.t(), t()}

Monty's propose_region: the region its goals are in.

receive_votes(votes, lm)

@spec receive_votes(term(), t()) :: {:ok, t()}

Monty's receive_votes: another module ruling an object out.

reset_stm(lm)

@spec reset_stm(t()) :: t()

The same, with the buffer and the paths emptied first — Monty's reset_stm.

send_out_vote(lm)

@spec send_out_vote(t()) :: {MapSet.t(), t()}

Monty's send_out_vote: the objects this module knows this one is not.

set_experiment_mode(lm, mode)

Tells the module whether this episode trains or evaluates.

set_primary_target(state, primary_target)

@spec set_primary_target(t(), map() | nil) :: t()

Names this object without emptying the module's history.

set_stepwise_target(lm, label)

@spec set_stepwise_target(t(), String.t() | nil) :: t()

Records which object the module's sensor is on this step.

set_target(lm, primary_target)

@spec set_target(t(), map() | nil) :: t()

Monty's fixme_reset_ground_truth: the object this episode is about.

set_terminal_state(lm, terminal_state)

@spec set_terminal_state(
  t(),
  Monty.Frameworks.Models.GraphMatching.GraphLM.terminal_state()
) :: t()

Sets the conclusion without asking the module to reconsider.

state_dict(lm)

@spec state_dict(t()) :: map()

Monty's state_dict: the memory, and which objects it was built from.

supervise(lm, target, location_on_model)

@spec supervise(t(), map(), [number()]) :: t()

The supervised conclusion an experiment states, in place of matching one.

update_ltm_from_stm(lm)

@spec update_ltm_from_stm(t()) :: t()

Monty's update_ltm_from_stm: a training episode's observations, into memory.

update_stats_after_vote(state)

@spec update_stats_after_vote(t()) :: t()

Monty's update_stats_after_vote: what a vote round left it believing.

update_terminal_condition(lm)

@spec update_terminal_condition(t()) :: {t(), atom() | nil}

Monty's update_terminal_condition: has this module concluded?

The decision is GraphLM's; what a displacement module adds is the pose, which Monty records as a side effect of being asked. So it is worked out first, here, and handed to the base as the answer to "is the pose narrowed down" — one place that computes it, and the conclusion the same as Monty's.