Current section

Files

Jump to
macula_neuroevolution include lc_chain.hrl
Raw

include/lc_chain.hrl

%% @doc Records for the Chained LTC Controller.
%%
%% The LC Chain consists of three LTC TWEANN levels that cascade:
%% L2 (Strategic) → L1 (Tactical) → L0 (Reactive) → Hyperparameters
%%
%% @author Macula.io
%% @copyright 2025 Macula.io
-ifndef(LC_CHAIN_HRL).
-define(LC_CHAIN_HRL, true).
%%% ============================================================================
%%% Time Constants
%%% ============================================================================
%% Time constants for each level (generations as time unit)
%% Higher τ = slower adaptation = more stable
-define(LC_L2_TAU, 100.0). % Strategic: very slow
-define(LC_L1_TAU, 50.0). % Tactical: medium
-define(LC_L0_TAU, 10.0). % Reactive: fast
%%% ============================================================================
%%% Chain Configuration
%%% ============================================================================
%% @doc Configuration for the LC chain.
-record(lc_chain_config, {
%% Time constants (can override defaults)
l2_tau = ?LC_L2_TAU :: float(),
l1_tau = ?LC_L1_TAU :: float(),
l0_tau = ?LC_L0_TAU :: float(),
%% Learning rate for weight updates
learning_rate = 0.001 :: float(),
%% Whether to evolve LC network topologies
evolve_topology = true :: boolean(),
%% Initial hidden layer sizes for each level
%% Format: [Layer1Size, Layer2Size, ...]
l2_hidden_layers = [8, 4] :: [pos_integer()],
l1_hidden_layers = [6, 4] :: [pos_integer()],
l0_hidden_layers = [10, 6] :: [pos_integer()],
%% Activation function for LTC neurons
activation = tanh :: tanh | sigmoid | relu,
%% Reward discount factor (for temporal credit assignment)
gamma = 0.95 :: float()
}).
-type lc_chain_config() :: #lc_chain_config{}.
%%% ============================================================================
%%% Chain State
%%% ============================================================================
%% @doc State of a single LC level (L0, L1, or L2).
-record(lc_level_state, {
%% Level identifier
level :: l0 | l1 | l2,
%% Agent ID in genotype DB (for TWEANN operations)
agent_id :: term(),
%% Time constant for this level
tau :: float(),
%% Internal LTC state for each neuron
%% Map: NeuronId => InternalState (float)
neuron_states = #{} :: #{term() => float()},
%% Last output values (cached for chaining)
last_outputs = [] :: [float()],
%% Connected sensor IDs (for emergent sensor tracking in L0)
connected_sensors = [] :: [atom()],
%% Generation counter
generation = 0 :: non_neg_integer()
}).
-type lc_level_state() :: #lc_level_state{}.
%% @doc State of the complete LC chain.
-record(lc_chain_state, {
%% Configuration
config :: lc_chain_config(),
%% States for each level
l2_state :: lc_level_state(),
l1_state :: lc_level_state(),
l0_state :: lc_level_state(),
%% Last computed hyperparameters
last_hyperparams = #{} :: #{atom() => float()},
%% Training history for reward computation
%% List of {Generation, Reward} tuples
reward_history = [] :: [{non_neg_integer(), float()}],
%% Cumulative reward for current episode
cumulative_reward = 0.0 :: float(),
%% Running flag
running = false :: boolean()
}).
-type lc_chain_state() :: #lc_chain_state{}.
%%% ============================================================================
%%% Evolution Metrics (L2 Inputs)
%%% ============================================================================
%% @doc Evolution metrics passed to L2.
%% These are normalized to [0, 1] range before feeding to network.
-record(evolution_metrics, {
best_fitness = 0.0 :: float(),
avg_fitness = 0.0 :: float(),
fitness_improvement = 0.0 :: float(),
fitness_variance = 0.0 :: float(),
stagnation_counter = 0 :: non_neg_integer(),
generation_progress = 0.0 :: float(), % current_gen / max_gen
population_diversity = 0.0 :: float(),
species_count = 1 :: pos_integer()
}).
-type evolution_metrics() :: #evolution_metrics{}.
%%% ============================================================================
%%% Emergent Metrics (L0 Additional Inputs)
%%% ============================================================================
%% @doc Emergent metrics from the model under training.
%% These are available for L0 to sense via topology evolution.
-record(emergent_metrics, {
%% Convergence metrics
convergence_rate = 0.0 :: float(),
fitness_plateau_duration = 0 :: non_neg_integer(),
%% Current hyperparameter feedback
current_mutation_rate = 0.1 :: float(),
current_selection_ratio = 0.2 :: float(),
%% Population dynamics
survival_rate = 0.0 :: float(),
offspring_rate = 0.0 :: float(),
elite_age = 0 :: non_neg_integer(),
%% Topology metrics
complexity_trend = 0.0 :: float(),
avg_network_size = 0.0 :: float(),
%% Species metrics
species_extinction_rate = 0.0 :: float(),
species_creation_rate = 0.0 :: float(),
%% Innovation metrics
innovation_rate = 0.0 :: float(),
diversity_index = 0.0 :: float()
}).
-type emergent_metrics() :: #emergent_metrics{}.
%%% ============================================================================
%%% Hyperparameter Output
%%% ============================================================================
%% @doc Hyperparameters output by L0.
%% These control the model under training.
-record(lc_hyperparams, {
mutation_rate = 0.1 :: float(), % [0.01, 0.5]
mutation_strength = 0.3 :: float(), % [0.05, 1.0]
selection_ratio = 0.2 :: float(), % [0.1, 0.5]
add_node_rate = 0.03 :: float(), % [0.0, 0.1]
add_connection_rate = 0.05 :: float() % [0.0, 0.2]
}).
-type lc_hyperparams() :: #lc_hyperparams{}.
%%% ============================================================================
%%% Default Hyperparameters
%%% ============================================================================
-define(LC_DEFAULT_HYPERPARAMS, #lc_hyperparams{
mutation_rate = 0.1,
mutation_strength = 0.3,
selection_ratio = 0.2,
add_node_rate = 0.03,
add_connection_rate = 0.05
}).
-endif. %% LC_CHAIN_HRL