Current section
Files
Jump to
Current section
Files
src/morphology.erl
%% @doc Morphology module for sensor/actuator specifications.
%%
%% This module defines the available sensors and actuators for each morphology
%% (problem domain). A morphology specifies what I/O interface the neural network
%% will use to interact with its environment.
%%
%% Based on DXNN2 by Gene Sher ("Handbook of Neuroevolution through Erlang").
%%
%% == Morphology Pattern ==
%%
%% Each morphology is implemented as a function clause that returns either
%% sensors or actuators depending on the argument:
%%
%%
%% morphology_name(sensors) -> [#sensor{...}];
%% morphology_name(actuators) -> [#actuator{...}].
%%
%% == Adding New Morphologies ==
%%
%% 1. Add export for the new morphology function
%% 2. Implement morphology_name(sensors) returning sensor list
%% 3. Implement morphology_name(actuators) returning actuator list
%% 4. Sensors need: name, type, scape, vl (vector length)
%% 5. Actuators need: name, type, scape, vl
%%
%% @author Macula.io
%% @copyright 2025 Macula.io, Apache-2.0
-module(morphology).
-include("records.hrl").
%% Suppress supertype warnings - specs are intentionally general for polymorphic returns
-dialyzer({nowarn_function, [
xor_mimic/1,
pole_balancing/1,
discrete_tmaze/1,
prey/1,
predator/1,
forex_trader/1
]}).
%% API
-export([
get_InitSensors/1,
get_InitActuators/1,
get_Sensors/1,
get_Actuators/1
]).
%% Morphology definitions
-export([
xor_mimic/1,
pole_balancing/1,
discrete_tmaze/1,
prey/1,
predator/1,
forex_trader/1
]).
%%==============================================================================
%% API Functions
%%==============================================================================
%% @doc Get initial sensors for a morphology.
%%
%% Returns the first sensor from the morphology's sensor list.
%% Used when constructing a new agent to get the default sensor.
%%
%% @param Morphology The morphology name (atom) or {Module, Function} tuple
%% @returns List containing the first sensor record
-spec get_InitSensors(atom() | {module(), atom()}) -> [#sensor{}].
get_InitSensors({M, F}) ->
Sensors = M:F(sensors),
[hd(Sensors)];
get_InitSensors(Morphology) ->
Sensors = ?MODULE:Morphology(sensors),
[hd(Sensors)].
%% @doc Get initial actuators for a morphology.
%%
%% Returns the first actuator from the morphology's actuator list.
%% Used when constructing a new agent to get the default actuator.
%%
%% @param Morphology The morphology name (atom) or {Module, Function} tuple
%% @returns List containing the first actuator record
-spec get_InitActuators(atom() | {module(), atom()}) -> [#actuator{}].
get_InitActuators({M, F}) ->
Actuators = M:F(actuators),
[hd(Actuators)];
get_InitActuators(Morphology) ->
Actuators = ?MODULE:Morphology(actuators),
[hd(Actuators)].
%% @doc Get all sensors for a morphology.
%%
%% @param Morphology The morphology name (atom) or {Module, Function} tuple
%% @returns List of all sensor records for this morphology
-spec get_Sensors(atom() | {module(), atom()}) -> [#sensor{}].
get_Sensors({M, F}) ->
M:F(sensors);
get_Sensors(Morphology) ->
?MODULE:Morphology(sensors).
%% @doc Get all actuators for a morphology.
%%
%% @param Morphology The morphology name (atom) or {Module, Function} tuple
%% @returns List of all actuator records for this morphology
-spec get_Actuators(atom() | {module(), atom()}) -> [#actuator{}].
get_Actuators({M, F}) ->
M:F(actuators);
get_Actuators(Morphology) ->
?MODULE:Morphology(actuators).
%%==============================================================================
%% Morphology Definitions
%%==============================================================================
%% @doc XOR mimic morphology for testing.
%%
%% Simple morphology that interfaces with an XOR simulation.
%% The sensor receives 2 inputs (the XOR inputs).
%% The actuator outputs 1 value (the XOR result).
%%
%% @param Type Either sensors or actuators
%% @returns List of sensor or actuator records
-spec xor_mimic(sensors | actuators) -> [#sensor{}] | [#actuator{}].
xor_mimic(sensors) ->
[
#sensor{
name = xor_GetInput,
type = standard,
scape = {private, xor_sim},
vl = 2
}
];
xor_mimic(actuators) ->
[
#actuator{
name = xor_SendOutput,
type = standard,
scape = {private, xor_sim},
vl = 1
}
].
%% @doc Pole balancing morphology.
%%
%% Classic reinforcement learning benchmark where the agent balances
%% one or more poles on a cart.
%%
%% @param Type Either sensors or actuators
%% @returns List of sensor or actuator records
-spec pole_balancing(sensors | actuators) -> [#sensor{}] | [#actuator{}].
pole_balancing(sensors) ->
[
#sensor{
name = pb_GetInput,
type = standard,
scape = {private, pb_sim},
vl = 3,
parameters = [3]
}
];
pole_balancing(actuators) ->
[
#actuator{
name = pb_SendOutput,
type = standard,
scape = {private, pb_sim},
vl = 1,
parameters = [with_damping, 1]
}
].
%% @doc Discrete T-Maze morphology.
%%
%% Navigation task where agent must learn to navigate a T-shaped maze.
%%
%% @param Type Either sensors or actuators
%% @returns List of sensor or actuator records
-spec discrete_tmaze(sensors | actuators) -> [#sensor{}] | [#actuator{}].
discrete_tmaze(sensors) ->
[
#sensor{
name = dtm_GetInput,
type = standard,
scape = {private, dtm_sim},
vl = 4,
parameters = [all]
}
];
discrete_tmaze(actuators) ->
[
#actuator{
name = dtm_SendOutput,
type = standard,
scape = {private, dtm_sim},
vl = 1,
parameters = []
}
].
%% @doc Prey morphology for predator-prey simulation.
%%
%% Flatland agent that can sense distance and move with two wheels.
%%
%% @param Type Either sensors or actuators
%% @returns List of sensor or actuator records
-spec prey(sensors | actuators) -> [#sensor{}] | [#actuator{}].
prey(sensors) ->
Pi = math:pi(),
Spread = Pi / 2,
Density = 5,
ROffset = 0,
[
#sensor{
name = distance_scanner,
type = standard,
scape = {public, flatland},
format = no_geo,
vl = Density,
parameters = [Spread, Density, ROffset]
}
];
prey(actuators) ->
[
#actuator{
name = two_wheels,
type = standard,
scape = {public, flatland},
format = no_geo,
vl = 2,
parameters = [2]
}
].
%% @doc Predator morphology for predator-prey simulation.
%%
%% Same as prey morphology (predator and prey have same I/O interface).
%%
%% @param Type Either sensors or actuators
%% @returns List of sensor or actuator records
-spec predator(sensors | actuators) -> [#sensor{}] | [#actuator{}].
predator(sensors) ->
prey(sensors);
predator(actuators) ->
prey(actuators).
%% @doc Forex trader morphology.
%%
%% Financial trading agent that reads price indicators and makes trades.
%%
%% @param Type Either sensors or actuators
%% @returns List of sensor or actuator records
-spec forex_trader(sensors | actuators) -> [#sensor{}] | [#actuator{}].
forex_trader(sensors) ->
HRes = 100,
[
#sensor{
name = fx_PLI,
type = standard,
scape = {private, fx_sim},
format = no_geo,
vl = HRes,
parameters = [HRes, close]
}
];
forex_trader(actuators) ->
[
#actuator{
name = fx_Trade,
type = standard,
scape = {private, fx_sim},
format = no_geo,
vl = 1,
parameters = []
}
].