The timestep coordinator: Monty's MontyBase.step as a sequence of barriers.
Everything that defines when a module is asked something lives here, in the
order Monty defines (aggregate_sensory_inputs → _step_learning_modules →
_vote → _pass_goals → _step_motor_system). The modules themselves are
processes (Monty.Runtime.SensorModuleServer,
Monty.Runtime.LearningModuleServer) with pure callbacks, so this module
reads as the algorithm and the modules stay testable on their own.
The coordinator talks to modules only through their public APIs, never
through raw GenServer messages. The wiring it needs between steps lives in
Monty.Runtime.Step.State, normalised once at start.
Rules inherited from Monty, and tested here rather than assumed:
- a learning module gets sensor percepts whose
pass_messageis true; - it gets another learning module's output only if it also got a sensor percept that step;
- every module sends a vote before any module receives one;
- the action chosen at step t belongs to step t+1;
- a module with no inputs is not stepped at all, and a step that left every module with nothing to process is not counted as a matching or exploratory step — which includes a step that handed a module a location and no features.
The coordinator is also the model Monty's experiment talks to
(ExperimentMonty): it owns the experiment mode, the step type, the step
counters, the terminal states, and the decision to stop. MontyBase keeps
that on the model and lets the experiment read it; here the experiment asks
for it (done?/1) and tells it what to do (reset_episode/1,
set_experiment_mode/2, update_ltm/1), because the coordinator is the
process that holds it.
Summary
Functions
Returns a specification to start this module under a supervisor.
The modules' own states, and the motor system, for a detailed log.
Whether the model has concluded this episode.
Runs fun against every learning module's server, for its side effect.
Runs fun against every sensor module's server, for its side effect.
Tells every module which object this episode is about.
The world's map from a percept's semantic id to the object's name.
Clears short-term memory for a new episode, keeping long-term memory.
Monty's MontyBase.reset: the modules forget the episode they just had, the
motor system forgets where it was in its policy, and the counters and the
terminal states start again.
One episode's numbers start again, and nothing else does.
Tells the model and its modules whether this episode trains or evaluates.
Monty's fixme_set_ground_truth: the object this episode is about, told to
every module, and the world's names for its semantic ids.
The coordinator's state, for tests and the run inspector.
The four things a recognition policy may read off the model.
Runs one step and returns the actions for the next environment step.
Tells the modules the episode's answer, as a supervised experiment knows it.
Switches the model into exploratory mode.
Ends the episode without a conclusion.
Hands what the episode saw to long-term memory.
Types
@type detailed() :: %{ learning_modules: [ %{id: Monty.Runtime.Step.State.id(), module: module(), state: term()} ], sensor_modules: [ %{id: Monty.Runtime.Step.State.id(), module: module(), state: term()} ], motor: struct() | nil }
The model's modules as they stand now.
Monty's logger reads model.learning_modules[i].buffer because its model holds
the modules; here the modules are processes, and the coordinator is what owns
their ids. So this is the coordinator asking each of them, once — which is the
one moment a run needs a module's whole state, at the end of an episode.
@type info() :: %{ step: non_neg_integer(), percept_ids: [atom()], inputs: %{optional(atom()) => non_neg_integer()}, processed?: boolean(), votes: map(), goals: non_neg_integer(), actions: [map()], sensor_timeouts: [atom()], step_type: Monty.Runtime.Step.State.step_type(), terminal_states: %{ optional(atom()) => Monty.Runtime.Step.State.terminal_state() }, done?: boolean(), duration_us: non_neg_integer() }
@type server() :: GenServer.server()
Functions
Returns a specification to start this module under a supervisor.
See Supervisor.
The modules' own states, and the motor system, for a detailed log.
projection runs inside each module's own process, so a logger that reads one
number out of a module holding a whole hypothesis space asks for that number
instead of for the space. & &1 asks for the state itself: the DETAILED log
writes an episode in full and is the one caller that wants it, and it is the
one caller that pays for the copy.
Whether the model has concluded this episode.
Monty's model.is_done: set by the model when its match criterion is
satisfied, or by the experiment when it gives up on an episode.
@spec exec_learning_modules(Monty.Runtime.Step.State.t(), (GenServer.server() -> any())) :: Monty.Runtime.Step.State.t()
Runs fun against every learning module's server, for its side effect.
@spec exec_sensor_modules(Monty.Runtime.Step.State.t(), (GenServer.server() -> any())) :: Monty.Runtime.Step.State.t()
Runs fun against every sensor module's server, for its side effect.
Tells every module which object this episode is about.
Monty's MontyForGraphMatching.fixme_set_ground_truth: the experiment knows
it, the modules cannot work it out, and without it nothing the episode does can
be scored.
@spec put_semantic_id_to_label(Monty.Runtime.Step.State.t(), keyword()) :: Monty.Runtime.Step.State.t()
The world's map from a percept's semantic id to the object's name.
Only the experiment knows it, and it is what lets a step say which object was under the patch rather than only that one was.
@spec reset_episode(server()) :: :ok
Clears short-term memory for a new episode, keeping long-term memory.
Monty's model.reset/0: the modules forget the episode they just had, the
motor system forgets where it was in its policy, and the counters and
terminal states start again. The step type returns to matching — an episode
that starts by exploring is an experiment's choice, not a model's default.
@spec reset_episode_state(Monty.Runtime.Step.State.t()) :: Monty.Runtime.Step.State.t()
Monty's MontyBase.reset: the modules forget the episode they just had, the
motor system forgets where it was in its policy, and the counters and the
terminal states start again.
Long-term memory is untouched, which is why an episode can end mid-object and the next one still knows what a mug looks like.
@spec restart_counters(Monty.Runtime.Step.State.t()) :: Monty.Runtime.Step.State.t()
One episode's numbers start again, and nothing else does.
Monty's reset_episode_steps/0, switch_to_matching_step/0 and
_is_done = False — what makes the next stretch of steps an episode, which is
the half of a reset a run without boundaries still performs.
@spec set_experiment_mode( server(), Monty.Frameworks.Models.AbstractMontyClasses.LearningModule.mode() ) :: :ok
Tells the model and its modules whether this episode trains or evaluates.
@spec set_ground_truth_state(Monty.Runtime.Step.State.t(), map() | nil, keyword()) :: Monty.Runtime.Step.State.t()
Monty's fixme_set_ground_truth: the object this episode is about, told to
every module, and the world's names for its semantic ids.
Monty's own comment: the modules cannot work this out, and without it nothing the episode does can be scored.
@spec start_link(map()) :: GenServer.on_start()
@spec state(server()) :: Monty.Runtime.Step.State.t()
The coordinator's state, for tests and the run inspector.
@spec status(server()) :: Monty.Experiment.RecognitionStrategy.model()
The four things a recognition policy may read off the model.
Monty's MontyIsDone and its siblings read model.is_done, the model's step
type, its matching-step count and each module's conclusion — four fields, not
the model. The coordinator holds the pretrained memory in its own state, so
answering with the whole thing would copy tens of megabytes per step to
deliver four values (docs/monty-differences.md §2.17).
Runs one step and returns the actions for the next environment step.
Tells the modules the episode's answer, as a supervised experiment knows it.
Monty's _pass_target_info_to_model. The modules supervised_ids names are
given the object, the pose it was placed with and where it stood; the others
have their hypotheses dropped, because a guess is not worth learning from.
@spec switch_to_exploratory_step(server()) :: :ok
Switches the model into exploratory mode.
Monty's model.switch_to_exploratory_step(), which a recognition policy asks
for with start_exploring — how a training episode keeps moving over an
object it has already recognised.
@spec time_out(server()) :: :ok
Ends the episode without a conclusion.
Monty's deal_with_time_out with global_time_out=True: the modules that
never concluded are timed out, so an episode's statistics say "no answer"
rather than leaving them open, and the model calls itself done.
@spec update_ltm(server()) :: :ok
Hands what the episode saw to long-term memory.
Called at the end of an episode: it is the only moment a training episode writes to memory, and the only moment an evaluation episode could.