agent_bridge (macula_neuroevolution v0.28.0)

View Source

Agent Bridge - Orchestrates the Sense→Think→Act Cycle.

This module ties together agent definition, sensors, actuators, and environment to run complete evaluation episodes. It is the integration point between domain-defined behaviours and the neuroevolution engine.

The bridge answers the question: "HOW do all the pieces fit together?"

Overview

The agent bridge:

  • Registers sensors and actuators for an agent type
  • Validates that topology matches I/O counts
  • Orchestrates the sense→think→act cycle
  • Slices inputs/outputs correctly for each sensor/actuator

Bridge Configuration

A bridge config specifies all components of an agent:

  Config = #{
      definition => my_agent_definition,
      sensors => [vision_sensor, hearing_sensor, energy_sensor],
      actuators => [movement_actuator, signal_actuator],
      environment => hex_arena_env
  }

Sense→Think→Act Cycle

Each tick, the bridge executes:

  1. SENSE: For each sensor, call read/2  collect inputs
  2. THINK: Feed inputs to neural network  get outputs
  3. ACT: For each actuator, slice outputs  call act/3  collect actions
  4. APPLY: For each action, call environment:apply_action/3

Input/Output Slicing

Sensors and actuators are processed in registration order:

  Sensors: [vision(18), hearing(4), energy(1)]  Inputs: [0..17, 18..21, 22]
  Actuators: [movement(7), signal(1)]  Outputs: [0..6, 7]

Topology Validation

The bridge validates that:

  • Sum of sensor input_counts == topology inputs
  • Sum of actuator output_counts == topology outputs

This catches configuration errors before training begins.

See also: agent_actuator, agent_definition, agent_environment, agent_sensor.

Summary

Functions

Processes outputs through all actuators.

Creates and validates a new bridge configuration.

Runs a complete evaluation episode.

Collects inputs from all sensors.

Executes one complete sense→think→act cycle.

Validates a bridge configuration.

Types

agent_state/0

-type agent_state() :: map().

bridge_config/0

-type bridge_config() ::
          #{definition := module(),
            sensors := [module()],
            actuators := [module()],
            environment := module(),
            evaluator => module()}.

env_state/0

-type env_state() :: map().

network/0

-type network() :: term().

Neural network (from macula_tweann).

validated_bridge/0

-type validated_bridge() ::
          #{definition := module(),
            sensors := [{module(), non_neg_integer(), pos_integer()}],
            actuators := [{module(), non_neg_integer(), pos_integer()}],
            environment := module(),
            evaluator => module(),
            total_inputs := pos_integer(),
            total_outputs := pos_integer(),
            topology := {pos_integer(), [pos_integer()], pos_integer()}}.

Functions

act(Bridge, Outputs, AgentState, EnvState)

-spec act(Bridge, Outputs, AgentState, EnvState) -> Actions
             when
                 Bridge :: validated_bridge(),
                 Outputs :: [float()],
                 AgentState :: agent_state(),
                 EnvState :: env_state(),
                 Actions :: [map()].

Processes outputs through all actuators.

Slices the output vector and calls each actuator's act/3. Returns a list of actions to apply.

new(Config)

-spec new(Config) -> {ok, ValidatedBridge} | {error, Reason}
             when Config :: bridge_config(), ValidatedBridge :: validated_bridge(), Reason :: term().

Creates and validates a new bridge configuration.

Returns a validated bridge with computed I/O offsets, or an error if validation fails.

Example:

  Config = #{
      definition => my_agent,
      sensors => [vision_sensor, energy_sensor],
      actuators => [movement_actuator],
      environment => arena_env
  },
  {ok, Bridge} = agent_bridge:new(Config).

run_episode(Bridge, Network, EnvConfig)

-spec run_episode(Bridge, Network, EnvConfig) -> Result
                     when
                         Bridge :: validated_bridge(),
                         Network :: network(),
                         EnvConfig :: map(),
                         Result :: {ok, float(), map()} | {ok, map()} | {error, term()}.

Runs a complete evaluation episode.

Executes the full episode lifecycle: 1. Initialize environment 2. Spawn agent 3. Loop: tick → sense → think → act → apply actions 4. Extract metrics when terminal 5. Calculate fitness if evaluator is configured

Returns: - {ok, Fitness, Metrics} if evaluator is configured - {ok, Metrics} if no evaluator (backward compatible)

sense(Bridge, AgentState, EnvState)

-spec sense(Bridge, AgentState, EnvState) -> Inputs
               when
                   Bridge :: validated_bridge(),
                   AgentState :: agent_state(),
                   EnvState :: env_state(),
                   Inputs :: [float()].

Collects inputs from all sensors.

Calls each sensor's read/2 in order and concatenates the results. Returns a flat list of floats ready for the neural network.

sense_think_act(Bridge, Network, AgentState, EnvState)

-spec sense_think_act(Bridge, Network, AgentState, EnvState) -> {Inputs, Outputs, Actions}
                         when
                             Bridge :: validated_bridge(),
                             Network :: network(),
                             AgentState :: agent_state(),
                             EnvState :: env_state(),
                             Inputs :: [float()],
                             Outputs :: [float()],
                             Actions :: [map()].

Executes one complete sense→think→act cycle.

This is the core function called each tick: 1. Sense: Collect inputs from all sensors 2. Think: Evaluate neural network 3. Act: Convert outputs to actions

Returns the list of actions to apply to the environment.

validate(Config)

-spec validate(Config) -> {ok, ValidatedBridge} | {error, Reason}
                  when
                      Config :: bridge_config(), ValidatedBridge :: validated_bridge(), Reason :: term().

Validates a bridge configuration.

Checks:

  • All modules implement their respective behaviours
  • Sum of sensor inputs matches topology inputs
  • Sum of actuator outputs matches topology outputs