Packages

Core mathematical functions for VIVA - sentient digital life. PAD emotions, Cusp catastrophe, Free Energy Principle, attractor dynamics.

Current section

Files

Jump to
viva_math src viva_math@common.erl
Raw

src/viva_math@common.erl

-module(viva_math@common).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/viva_math/common.gleam").
-export([clamp/3, clamp_unit/1, clamp_bipolar/1, lerp/3, inverse_lerp/3, sigmoid/2, sigmoid_standard/1, softmax/1, safe_div/3, smoothstep/3, exponential_decay/3, inverse_decay/3, inverse_sqrt_decay/3, deterministic_noise/2, wiener_increment/3]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" Common mathematical utilities for VIVA.\n"
"\n"
" Functions not found in gleam_community_maths:\n"
" - clamp, lerp, sigmoid, softmax, safe_div\n"
).
-file("src/viva_math/common.gleam", 19).
?DOC(
" Clamp a value to a range [min, max].\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" clamp(5.0, 0.0, 10.0) // -> 5.0\n"
" clamp(-1.0, 0.0, 10.0) // -> 0.0\n"
" clamp(15.0, 0.0, 10.0) // -> 10.0\n"
" ```\n"
).
-spec clamp(float(), float(), float()) -> float().
clamp(Value, Min, Max) ->
_pipe = Value,
_pipe@1 = gleam@float:max(_pipe, Min),
gleam@float:min(_pipe@1, Max).
-file("src/viva_math/common.gleam", 34).
?DOC(
" Clamp a value to the unit interval [0, 1].\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" clamp_unit(0.5) // -> 0.5\n"
" clamp_unit(-0.5) // -> 0.0\n"
" clamp_unit(1.5) // -> 1.0\n"
" ```\n"
).
-spec clamp_unit(float()) -> float().
clamp_unit(Value) ->
clamp(Value, +0.0, 1.0).
-file("src/viva_math/common.gleam", 48).
?DOC(
" Clamp a value to the bipolar interval [-1, 1].\n"
" Used for PAD dimensions.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" clamp_bipolar(0.5) // -> 0.5\n"
" clamp_bipolar(-1.5) // -> -1.0\n"
" clamp_bipolar(1.5) // -> 1.0\n"
" ```\n"
).
-spec clamp_bipolar(float()) -> float().
clamp_bipolar(Value) ->
clamp(Value, -1.0, 1.0).
-file("src/viva_math/common.gleam", 64).
?DOC(
" Linear interpolation between two values.\n"
"\n"
" lerp(a, b, 0.0) = a\n"
" lerp(a, b, 1.0) = b\n"
" lerp(a, b, 0.5) = (a + b) / 2\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" lerp(0.0, 10.0, 0.5) // -> 5.0\n"
" lerp(0.0, 10.0, 0.25) // -> 2.5\n"
" ```\n"
).
-spec lerp(float(), float(), float()) -> float().
lerp(A, B, T) ->
A + (T * (B - A)).
-file("src/viva_math/common.gleam", 77).
?DOC(
" Inverse linear interpolation - find t given value in range [a, b].\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" inverse_lerp(0.0, 10.0, 5.0) // -> Ok(0.5)\n"
" inverse_lerp(0.0, 10.0, 2.5) // -> Ok(0.25)\n"
" inverse_lerp(0.0, 0.0, 5.0) // -> Error(Nil) (division by zero)\n"
" ```\n"
).
-spec inverse_lerp(float(), float(), float()) -> {ok, float()} | {error, nil}.
inverse_lerp(A, B, Value) ->
Range = B - A,
case Range =:= +0.0 of
true ->
{error, nil};
false ->
{ok, case Range of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator -> (Value - A) / Gleam@denominator
end}
end.
-file("src/viva_math/common.gleam", 97).
?DOC(
" Sigmoid function: 1 / (1 + exp(-k * x))\n"
"\n"
" Maps any real number to (0, 1).\n"
" k controls steepness (k=1 is standard sigmoid).\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" sigmoid(0.0, 1.0) // -> 0.5\n"
" sigmoid(100.0, 1.0) // -> ~1.0\n"
" sigmoid(-100.0, 1.0) // -> ~0.0\n"
" ```\n"
).
-spec sigmoid(float(), float()) -> float().
sigmoid(X, K) ->
Neg_kx = +0.0 - (K * X),
case (1.0 + gleam_community@maths:exponential(Neg_kx)) of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator -> 1.0 / Gleam@denominator
end.
-file("src/viva_math/common.gleam", 103).
?DOC(" Standard sigmoid with k=1.\n").
-spec sigmoid_standard(float()) -> float().
sigmoid_standard(X) ->
sigmoid(X, 1.0).
-file("src/viva_math/common.gleam", 118).
?DOC(
" Softmax function: converts a list of values to probabilities.\n"
"\n"
" softmax([x1, x2, ...]) = [exp(x1)/sum, exp(x2)/sum, ...]\n"
" where sum = exp(x1) + exp(x2) + ...\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" softmax([1.0, 2.0, 3.0]) // -> [0.09, 0.24, 0.67] (approx)\n"
" softmax([0.0, 0.0]) // -> [0.5, 0.5]\n"
" ```\n"
).
-spec softmax(list(float())) -> {ok, list(float())} | {error, nil}.
softmax(Values) ->
case Values of
[] ->
{error, nil};
_ ->
Max_val = gleam@list:fold(Values, +0.0, fun gleam@float:max/2),
Exps = gleam@list:map(
Values,
fun(X) -> gleam_community@maths:exponential(X - Max_val) end
),
Sum = gleam@list:fold(Exps, +0.0, fun(Acc, X@1) -> Acc + X@1 end),
case Sum =:= +0.0 of
true ->
{error, nil};
false ->
{ok, gleam@list:map(Exps, fun(X@2) -> case Sum of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator -> X@2 / Gleam@denominator
end end)}
end
end.
-file("src/viva_math/common.gleam", 147).
?DOC(
" Safe division with default value on division by zero.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" safe_div(10.0, 2.0, 0.0) // -> 5.0\n"
" safe_div(10.0, 0.0, -1.0) // -> -1.0\n"
" ```\n"
).
-spec safe_div(float(), float(), float()) -> float().
safe_div(A, B, Default) ->
case B =:= +0.0 of
true ->
Default;
false ->
case B of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator -> A / Gleam@denominator
end
end.
-file("src/viva_math/common.gleam", 164).
?DOC(
" Smooth step function (Hermite interpolation).\n"
" Returns 0 if x < edge0, 1 if x > edge1, smooth transition otherwise.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" smoothstep(0.0, 1.0, 0.5) // -> 0.5 (roughly)\n"
" smoothstep(0.0, 1.0, 0.0) // -> 0.0\n"
" smoothstep(0.0, 1.0, 1.0) // -> 1.0\n"
" ```\n"
).
-spec smoothstep(float(), float(), float()) -> float().
smoothstep(Edge0, Edge1, X) ->
T = clamp_unit(case (Edge1 - Edge0) of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator -> (X - Edge0) / Gleam@denominator
end),
(T * T) * (3.0 - (2.0 * T)).
-file("src/viva_math/common.gleam", 177).
?DOC(
" Exponential decay: value * exp(-rate * time)\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" exponential_decay(1.0, 0.5, 1.0) // -> ~0.606\n"
" exponential_decay(1.0, 0.0, 10.0) // -> 1.0 (no decay)\n"
" ```\n"
).
-spec exponential_decay(float(), float(), float()) -> float().
exponential_decay(Value, Rate, Time) ->
Neg_rt = +0.0 - (Rate * Time),
Value * gleam_community@maths:exponential(Neg_rt).
-file("src/viva_math/common.gleam", 237).
?DOC(
" Inverse decay rate (pattern from viva_glyph/association.gleam).\n"
" η(t) = η₀ / (1 + t/τ)\n"
"\n"
" Used for learning rate decay, consolidation, etc.\n"
).
-spec inverse_decay(float(), float(), float()) -> float().
inverse_decay(Base_rate, T, Tau) ->
case Tau =< +0.0 of
true ->
Base_rate;
false ->
case (1.0 + (case Tau of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator -> T / Gleam@denominator
end)) of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator@1 -> Base_rate / Gleam@denominator@1
end
end.
-file("src/viva_math/common.gleam", 246).
?DOC(
" Inverse square root decay.\n"
" η(t) = η₀ / √(1 + t/τ)\n"
).
-spec inverse_sqrt_decay(float(), float(), float()) -> float().
inverse_sqrt_decay(Base_rate, T, Tau) ->
case Tau =< +0.0 of
true ->
Base_rate;
false ->
case gleam_community@maths:nth_root(1.0 + (case Tau of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator -> T / Gleam@denominator
end), 2) of
{ok, Sqrt_val} ->
case Sqrt_val of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator@1 -> Base_rate / Gleam@denominator@1
end;
{error, _} ->
Base_rate
end
end.
-file("src/viva_math/common.gleam", 259).
-spec int_to_float(integer()) -> float().
int_to_float(N) ->
case N of
0 ->
+0.0;
1 ->
1.0;
_ ->
case N < 0 of
true ->
+0.0 - int_to_float(0 - N);
false ->
Half = N div 2,
Remainder = N - (Half * 2),
(int_to_float(Half) * 2.0) + int_to_float(Remainder)
end
end.
-file("src/viva_math/common.gleam", 204).
?DOC(
" Deterministic pseudo-random noise generator.\n"
" Uses hash-based seeding for reproducibility (no external RNG needed).\n"
"\n"
" Returns value in [-1, 1] range.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" deterministic_noise(0, 42) // -> some value in [-1, 1]\n"
" deterministic_noise(0, 42) // -> same value (deterministic)\n"
" ```\n"
).
-spec deterministic_noise(integer(), integer()) -> float().
deterministic_noise(Step, Seed) ->
Hash = (((Step * 31) + (Seed * 17)) + 7919) rem 1000,
(int_to_float(Hash) - 500.0) / 500.0.
-file("src/viva_math/common.gleam", 225).
?DOC(
" Generate Wiener process increment (discrete approximation).\n"
" dW ≈ √dt × N(0,1) where N is approximated by deterministic noise.\n"
"\n"
" Used for stochastic differential equations.\n"
"\n"
" ## Parameters\n"
" - step: Current time step (for determinism)\n"
" - seed: Random seed\n"
" - dt: Time step size\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" wiener_increment(0, 42, 0.01) // -> small noise scaled by √dt\n"
" ```\n"
).
-spec wiener_increment(integer(), integer(), float()) -> float().
wiener_increment(Step, Seed, Dt) ->
Noise = deterministic_noise(Step, Seed),
case gleam_community@maths:nth_root(Dt, 2) of
{ok, Sqrt_dt} ->
Noise * Sqrt_dt;
{error, _} ->
+0.0
end.