Current section

Files

Jump to
macula_tweann src perturbation_utils.erl
Raw

src/perturbation_utils.erl

%% @doc Weight perturbation utilities for neural network evolution.
%%
%% Provides functions for perturbing weights during evolution:
%% - Single weight perturbation with momentum
%% - Batch weight perturbation
%% - Saturation to prevent numerical overflow
%%
%% == Weight Format ==
%%
%% Weights use the format: {Weight, DeltaWeight, LearningRate, ParameterList}
%% - Weight: Current synaptic weight value
%% - DeltaWeight: Previous change (for momentum)
%% - LearningRate: Plasticity learning rate
%% - ParameterList: Additional plasticity parameters
%%
%% @author Macula.io
%% @copyright 2025 Macula.io, Apache-2.0
-module(perturbation_utils).
-export([
perturb_weight/2,
perturb_weights/2,
sat/2
]).
%% Maximum weight value (saturation limit).
%% Weights are clamped to [-SAT_LIMIT, SAT_LIMIT] to prevent
%% numerical overflow and maintain network stability.
-define(SAT_LIMIT, math:pi() * 10).
%% @doc Perturb a single weight with momentum.
%%
%% Uses momentum-based perturbation:
%% DW_new = random_noise * spread + DW_old * 0.5
%% W_new = saturate(W + DW_new)
%%
%% The 0.5 momentum factor provides smooth weight trajectories
%% during evolution.
%%
%% @param WeightSpec weight tuple {W, DW, LP, LPs}
%% @param Spread perturbation range
%% @returns new weight tuple
-spec perturb_weight({float(), float(), float(), list()}, float()) ->
{float(), float(), float(), list()}.
perturb_weight({W, DW, LP, LPs}, Spread) ->
%% Random noise in [-0.5*Spread, 0.5*Spread] plus momentum
NewDW = (rand:uniform() - 0.5) * Spread + DW * 0.5,
NewW = sat(W + NewDW, ?SAT_LIMIT),
{NewW, NewDW, LP, LPs}.
%% @doc Perturb list of weight specs.
%%
%% Applies perturbation to each weight in the list.
%%
%% @param WeightSpecs list of weight tuples
%% @param Spread perturbation range
%% @returns list of perturbed weight tuples
-spec perturb_weights([{float(), float(), float(), list()}], float()) ->
[{float(), float(), float(), list()}].
perturb_weights(WeightSpecs, Spread) ->
[perturb_weight(WS, Spread) || WS <- WeightSpecs].
%% @doc Saturate a value to within +/- limit.
%%
%% Clamps the value to [-Limit, Limit] to prevent overflow.
%%
%% @param Value the value to saturate
%% @param Limit the saturation limit (positive)
%% @returns saturated value
-spec sat(float(), float()) -> float().
sat(Value, Limit) when Value > Limit ->
Limit;
sat(Value, Limit) when Value < -Limit ->
-Limit;
sat(Value, _Limit) ->
Value.