Monty. Frameworks. Models. GoalGeneration. GraphGoalGenerator
(ExTbpMonty.Core v0.1.0)
Copy Markdown
View Source
Generate sub-goals until the goal it is driving towards is achieved.
Mirrors tbp/monty/frameworks/models/goal_generation.py::GraphGoalGenerator,
the base goal-state generator (GSG). A GSG belongs to a learning module and
does one thing on every step: decide whether the goal it last output has been
achieved, and if so produce the next one. What "achieved" means is the only
interesting part, and the base class answers it roughly — the sensory input
changed, so something moved — which is enough for a module that is only
trying to reduce its own uncertainty.
This class generates no goals of its own: generate_goal/2 answers nil,
meaning "carry on, I only want more certainty". EvidenceGoalGenerator
overrides that with the real thing, which is where the work is.
Where the parent module went
Monty holds a pointer to the learning module and reads it whenever it likes —
self.parent_lm.get_output(). A struct cannot hold a live pointer to a value
that changes, so a caller hands the generator two things instead: the module
itself (for its buffer, its step counters and its tolerances) and, through
:parent_output, how to ask it for its current output. The default answers
nil, which is what a module with nothing to say would answer, and Monty's
only use of that call is comparing against a driving goal that experiments do
not set yet.
Summary
Functions
The buffer this generator reads from and records into.
The generator a config entry describes.
The statistics this generator keeps on its parent's buffer.
Records whether the goal in hand was achieved on this step.
Whether two messages differ by more than the given tolerances.
A generator with no goals yet.
Whether the last output goal was achieved, judged from the sensory input.
The goal this generator wants others to achieve, or none.
The learning module this generator reads, or an error when there is none.
The same generator, now reading parent_lm as it stands.
Points this generator at its learning module and resets it.
Forgets the goals held so far and starts the log over.
Receives the high-level goal to drive towards.
One step: was the last goal achieved, and if so what is next.
Types
@type t() :: %Monty.Frameworks.Models.GoalGeneration.GraphGoalGenerator{ driving_goal: Monty.Cmp.Goal.t() | nil, goal_tolerances: map(), keep_output_goal: (-> boolean()) | nil, output_goal: Monty.Cmp.Goal.t() | nil, parent_lm: map() | nil, parent_output: (-> term()) | nil }
Functions
@spec buffer(t()) :: Monty.Frameworks.Models.Buffer.FeatureAtLocationBuffer.t()
The buffer this generator reads from and records into.
Goals are logged on the parent module's buffer rather than on the generator, so that everything a step learned about a module is read from one place afterwards.
The generator a config entry describes.
On its own it emits no goal, which is what an experiment that only wants a learning module to keep matching asks for.
@spec goal_stat_keys() :: [String.t()]
The statistics this generator keeps on its parent's buffer.
Records whether the goal in hand was achieved on this step.
The goal was set — and could have been achieved — on the previous step, which is why the recorded matching step is the one before this.
@spec messages_different?( Monty.Cmp.Message.t() | nil, Monty.Cmp.Message.t() | nil, map() ) :: boolean()
Whether two messages differ by more than the given tolerances.
One feature outside its tolerance is enough. An undefined message, or an attribute both messages leave undefined, says nothing and is not a difference — which is what makes a goal with only a location meaningful.
A generator with no goals yet.
:goal_tolerances says how close is close enough per goal attribute — a
distance in metres for "location" unless an experiment says otherwise; it is
the GSG's own tolerance, not the learning module's matching tolerance, because
the question here is whether a goal was reached, not whether two things look
alike. :parent_output is how to ask the parent module for its output.
@spec output_goal_achieved?(t(), [Monty.Cmp.Message.t()]) :: boolean()
Whether the last output goal was achieved, judged from the sensory input.
The prediction is only "the input changed": a motor system that fails to reach its goal usually moves back, so a step that shows the same thing twice is a step where the agent went nowhere. That is approximate on purpose — the alternative needs a predicted percept, which needs a model of the world from a goal's point of view, and Monty's own comment says so.
@spec output_goals(t()) :: [Monty.Cmp.Goal.t()]
The goal this generator wants others to achieve, or none.
The learning module this generator reads, or an error when there is none.
The same generator, now reading parent_lm as it stands.
Monty holds a pointer to its module and so sees every change to it for free. A struct holds a copy, so whatever drives the generator hands it the module again before each step — without resetting, because the schedule the generator has built up still belongs to this module.
Points this generator at its learning module and resets it.
Setting the module resets, because goals left over from another module belong to another object.
Forgets the goals held so far and starts the log over.
The log lives on the parent module's buffer, which is why this needs one.
@spec set_driving_goal(t(), Monty.Cmp.Goal.t() | nil) :: t()
Receives the high-level goal to drive towards.
Monty's experiments never set one yet, so this stays nil: the default goal
is "be more certain", which the base class cannot check and therefore never
achieves — a module keeps gathering evidence for as long as the experiment
lets it.
@spec step(t(), Monty.Context.t(), [Monty.Cmp.Message.t()]) :: t()
One step: was the last goal achieved, and if so what is next.
Running out of goals is not an error — the module carries on with no goal, which for the base class is the whole of its behaviour.