Monty.Runtime.Step (ExTbpMonty.Core v0.1.0)

Copy Markdown View Source

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_message is 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

Types

The model's modules as they stand now.

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

detailed()

@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.

info()

@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()
}

model_id()

@type model_id() :: atom() | String.t()

server()

@type server() :: GenServer.server()

Functions

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

detailed(server, projection \\ & &1)

@spec detailed(server(), (term() -> term())) :: detailed()

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.

done?(server)

@spec done?(server()) :: boolean()

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.

exec_learning_modules(state, fun)

@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.

exec_sensor_modules(state, fun)

@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.

fixme_set_ground_truth(server, primary_target, opts \\ [])

@spec fixme_set_ground_truth(server(), map() | nil, keyword()) :: :ok

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.

put_semantic_id_to_label(state, opts)

@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.

reset_episode(server)

@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.

reset_episode_state(state)

@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.

restart_counters(state)

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.

set_experiment_mode(server, mode)

Tells the model and its modules whether this episode trains or evaluates.

set_ground_truth_state(state, primary_target, opts)

@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.

start_link(options)

@spec start_link(map()) :: GenServer.on_start()

state(server)

@spec state(server()) :: Monty.Runtime.Step.State.t()

The coordinator's state, for tests and the run inspector.

status(server)

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).

step(server, observations, proprioceptive_state, ctx)

@spec step(server(), map(), map(), Monty.Context.t()) :: {:ok, [map()], info()}

Runs one step and returns the actions for the next environment step.

supervise(server, target, location_on_model, supervised_ids)

@spec supervise(server(), map(), [number()], [String.t()]) :: :ok

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.

switch_to_exploratory_step(server)

@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.

time_out(server)

@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.

update_ltm(server)

@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.