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@scalar.erl
Raw

src/viva_math@scalar.erl

-module(viva_math@scalar).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/viva_math/scalar.gleam").
-export([erf/1, erfc/1, fmod/2, exp/1, expm1/1, ln/1, log1p/1, tanh/1, sqrt/1, pow/2, sin/1, cos/1, tan/1, asin/1, acos/1, atan2/2, log2/1, log10/1, logarithm/1, logarithm_2/1, logarithm_10/1, square_root/1, try_ln/1, try_log2/1, try_log10/1, try_sqrt/1, cbrt/1, cube_root/1, nth_root/2, try_cbrt/1, try_nth_root/2, safe_log/2, safe_exp/1, safe_sqrt/1, safe_div/3, logaddexp/2, logsumexp/1, hypot/2, sigmoid/1, logit/1, sigmoid_k/2, relu/1, leaky_relu/2, elu/2, selu/1, gelu/1, gelu_approx/1, silu/1, swish/1, softplus/1, mish/1, lambda_gelu/2, atan/1, iglu/2, iglu_approx/2, hard_sigmoid/1, hard_swish/1, hard_tanh/1, clamp/3, clamp_unit/1, clamp_bipolar/1, lerp/3, smoothstep/3, smootherstep/3, sign/1, step/2, deg_to_rad/1, rad_to_deg/1]).
-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(
" Scalar mathematical functions.\n"
"\n"
" This module provides pipeline-friendly scalar primitives commonly\n"
" needed for ML / scientific computing. Where the Erlang stdlib already\n"
" exposes a fast BIF (such as\n"
" `:math.erf/1`, `:math.erfc/1` or `:math.fmod/2`), we delegate through FFI\n"
" rather than reimplementing.\n"
"\n"
" ## Design rules\n"
"\n"
" - All functions take and return `Float` directly (no `Result`) unless a\n"
" genuine domain error is unavoidable.\n"
" - Numerically stable variants are preferred: `logsumexp`, `softplus` and\n"
" `safe_log/exp/sqrt` guard against overflow / NaN.\n"
" - Activations follow the conventions used by `viva_tensor`, `pytorch` and\n"
" `jax` so that `viva_tensor` can delegate scalar paths here directly.\n"
"\n"
" ## Erlang BIFs used (OTP 22+, confirmed on OTP 28)\n"
"\n"
" `:math.erf/1`, `:math.erfc/1`, `:math.fmod/2`, `:math.expm1/1`,\n"
" `:math.log1p/1`, `:math.tanh/1`, `:math.exp/1`, `:math.log/1`,\n"
" `:math.sqrt/1`, `:math.pow/2`.\n"
).
-file("src/viva_math/scalar.gleam", 37).
?DOC(
" Error function erf(x) = (2/√π) · ∫₀ˣ e^(-t²) dt.\n"
"\n"
" Delegates to `:math.erf/1` (Erlang stdlib BIF).\n"
).
-spec erf(float()) -> float().
erf(X) ->
math:erf(X).
-file("src/viva_math/scalar.gleam", 45).
?DOC(
" Complementary error function erfc(x) = 1 - erf(x).\n"
"\n"
" Computed by methods that avoid cancellation for large `x`.\n"
" Delegates to `:math.erfc/1`.\n"
).
-spec erfc(float()) -> float().
erfc(X) ->
math:erfc(X).
-file("src/viva_math/scalar.gleam", 52).
?DOC(
" Floating-point remainder `x mod y` (IEEE 754 fmod).\n"
"\n"
" Delegates to `:math.fmod/2`.\n"
).
-spec fmod(float(), float()) -> float().
fmod(X, Y) ->
math:fmod(X, Y).
-file("src/viva_math/scalar.gleam", 91).
?DOC(" Natural exponential e^x. Delegates to `:math.exp/1`.\n").
-spec exp(float()) -> float().
exp(X) ->
math:exp(X).
-file("src/viva_math/scalar.gleam", 60).
?DOC(
" `exp(x) - 1` accurate for small `x`.\n"
"\n"
" Uses a 4-term Maclaurin series near zero (|x| < 1e-5) to avoid the\n"
" catastrophic cancellation of `exp(x) - 1.0`. Falls back to the direct\n"
" expression elsewhere. (Not all OTP builds ship `:math.expm1/1`, so we\n"
" implement it portably.)\n"
).
-spec expm1(float()) -> float().
expm1(X) ->
case gleam@float:absolute_value(X) < 1.0e-5 of
true ->
(X + ((X * X) / 2.0)) + (((X * X) * X) / 6.0);
false ->
math:exp(X) - 1.0
end.
-file("src/viva_math/scalar.gleam", 96).
?DOC(" Natural logarithm ln(x). Domain x > 0.\n").
-spec ln(float()) -> float().
ln(X) ->
math:log(X).
-file("src/viva_math/scalar.gleam", 71).
?DOC(
" `log(1 + x)` accurate for small `x`.\n"
"\n"
" Uses a Maclaurin series near zero to avoid cancellation in `ln(1.0 + x)`.\n"
" Falls back to direct logarithm elsewhere.\n"
).
-spec log1p(float()) -> float().
log1p(X) ->
case gleam@float:absolute_value(X) < 1.0e-4 of
true ->
X2 = X * X,
X3 = X2 * X,
X4 = X3 * X,
((X - (X2 / 2.0)) + (X3 / 3.0)) - (X4 / 4.0);
false ->
math:log(1.0 + X)
end.
-file("src/viva_math/scalar.gleam", 86).
?DOC(" Hyperbolic tangent. Delegates to `:math.tanh/1`.\n").
-spec tanh(float()) -> float().
tanh(X) ->
math:tanh(X).
-file("src/viva_math/scalar.gleam", 101).
?DOC(" Square root.\n").
-spec sqrt(float()) -> float().
sqrt(X) ->
math:sqrt(X).
-file("src/viva_math/scalar.gleam", 106).
?DOC(" Power x^y.\n").
-spec pow(float(), float()) -> float().
pow(X, Y) ->
math:pow(X, Y).
-file("src/viva_math/scalar.gleam", 111).
?DOC(" Sine of `x` (radians). Delegates to `:math.sin/1`.\n").
-spec sin(float()) -> float().
sin(X) ->
math:sin(X).
-file("src/viva_math/scalar.gleam", 116).
?DOC(" Cosine of `x` (radians). Delegates to `:math.cos/1`.\n").
-spec cos(float()) -> float().
cos(X) ->
math:cos(X).
-file("src/viva_math/scalar.gleam", 121).
?DOC(" Tangent of `x` (radians). Delegates to `:math.tan/1`.\n").
-spec tan(float()) -> float().
tan(X) ->
math:tan(X).
-file("src/viva_math/scalar.gleam", 126).
?DOC(" Inverse sine. Domain `x ∈ [-1, 1]`; outside crashes (BIF behaviour).\n").
-spec asin(float()) -> float().
asin(X) ->
math:asin(X).
-file("src/viva_math/scalar.gleam", 131).
?DOC(" Inverse cosine. Domain `x ∈ [-1, 1]`; outside crashes (BIF behaviour).\n").
-spec acos(float()) -> float().
acos(X) ->
math:acos(X).
-file("src/viva_math/scalar.gleam", 136).
?DOC(" Two-argument arctangent `atan2(y, x)`.\n").
-spec atan2(float(), float()) -> float().
atan2(Y, X) ->
math:atan2(Y, X).
-file("src/viva_math/scalar.gleam", 141).
?DOC(" Log base 2. Delegates to `:math.log2/1`. Domain `x > 0`.\n").
-spec log2(float()) -> float().
log2(X) ->
math:log2(X).
-file("src/viva_math/scalar.gleam", 146).
?DOC(" Log base 10. Delegates to `:math.log10/1`. Domain `x > 0`.\n").
-spec log10(float()) -> float().
log10(X) ->
math:log10(X).
-file("src/viva_math/scalar.gleam", 160).
?DOC(" Natural logarithm with domain check. Returns `Error(Nil)` for `x ≤ 0`.\n").
-spec logarithm(float()) -> {ok, float()} | {error, nil}.
logarithm(X) ->
case X =< +0.0 of
true ->
{error, nil};
false ->
{ok, math:log(X)}
end.
-file("src/viva_math/scalar.gleam", 168).
?DOC(" Log base 2 with domain check. Returns `Error(Nil)` for `x ≤ 0`.\n").
-spec logarithm_2(float()) -> {ok, float()} | {error, nil}.
logarithm_2(X) ->
case X =< +0.0 of
true ->
{error, nil};
false ->
{ok, math:log2(X)}
end.
-file("src/viva_math/scalar.gleam", 176).
?DOC(" Log base 10 with domain check. Returns `Error(Nil)` for `x ≤ 0`.\n").
-spec logarithm_10(float()) -> {ok, float()} | {error, nil}.
logarithm_10(X) ->
case X =< +0.0 of
true ->
{error, nil};
false ->
{ok, math:log10(X)}
end.
-file("src/viva_math/scalar.gleam", 184).
?DOC(" Square root with domain check. Returns `Error(Nil)` for `x < 0`.\n").
-spec square_root(float()) -> {ok, float()} | {error, nil}.
square_root(X) ->
case X < +0.0 of
true ->
{error, nil};
false ->
{ok, math:sqrt(X)}
end.
-file("src/viva_math/scalar.gleam", 195).
?DOC(" Deprecated alias for `logarithm`.\n").
-spec try_ln(float()) -> {ok, float()} | {error, nil}.
try_ln(X) ->
logarithm(X).
-file("src/viva_math/scalar.gleam", 201).
?DOC(" Deprecated alias for `logarithm_2`.\n").
-spec try_log2(float()) -> {ok, float()} | {error, nil}.
try_log2(X) ->
logarithm_2(X).
-file("src/viva_math/scalar.gleam", 207).
?DOC(" Deprecated alias for `logarithm_10`.\n").
-spec try_log10(float()) -> {ok, float()} | {error, nil}.
try_log10(X) ->
logarithm_10(X).
-file("src/viva_math/scalar.gleam", 213).
?DOC(" Deprecated alias for `square_root`.\n").
-spec try_sqrt(float()) -> {ok, float()} | {error, nil}.
try_sqrt(X) ->
square_root(X).
-file("src/viva_math/scalar.gleam", 218).
?DOC(" Real cube root, defined for all `x` via `sign(x) · |x|^(1/3)`.\n").
-spec cbrt(float()) -> float().
cbrt(X) ->
case X of
X@1 when X@1 > +0.0 ->
math:pow(X@1, 1.0 / 3.0);
X@2 when X@2 < +0.0 ->
+0.0 - math:pow(+0.0 - X@2, 1.0 / 3.0);
_ ->
+0.0
end.
-file("src/viva_math/scalar.gleam", 228).
?DOC(
" Real cube root with `Result` API for parity with `square_root`.\n"
" Total over `Float`, so `Ok` always; kept for ergonomic chaining.\n"
).
-spec cube_root(float()) -> {ok, float()} | {error, nil}.
cube_root(X) ->
{ok, cbrt(X)}.
-file("src/viva_math/scalar.gleam", 239).
?DOC(
" Generalised real `n`-th root (`x^(1/n)`) for integer `n ≥ 1`.\n"
"\n"
" - `n = 1` → returns `Ok(x)`.\n"
" - `n = 2` → uses `square_root` (errors for `x < 0`).\n"
" - Odd `n` → defined for all real `x` (sign trick).\n"
" - Even `n > 2` → errors for `x < 0`; uses `pow(x, 1/n)` otherwise.\n"
" - `n ≤ 0` → `Error(Nil)`.\n"
).
-spec nth_root(float(), integer()) -> {ok, float()} | {error, nil}.
nth_root(X, N) ->
case N of
N@1 when N@1 =< 0 ->
{error, nil};
1 ->
{ok, X};
2 ->
square_root(X);
N@2 ->
Is_odd = (N@2 rem 2) /= 0,
case {X < +0.0, Is_odd} of
{true, false} ->
{error, nil};
{true, true} ->
{ok, +0.0 - math:pow(+0.0 - X, case erlang:float(N@2) of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator -> 1.0 / Gleam@denominator
end)};
{false, _} ->
{ok, math:pow(X, case erlang:float(N@2) of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator@1 -> 1.0 / Gleam@denominator@1
end)}
end
end.
-file("src/viva_math/scalar.gleam", 259).
?DOC(" Deprecated alias for `cube_root`.\n").
-spec try_cbrt(float()) -> {ok, float()} | {error, nil}.
try_cbrt(X) ->
cube_root(X).
-file("src/viva_math/scalar.gleam", 265).
?DOC(" Deprecated alias for `nth_root`.\n").
-spec try_nth_root(float(), integer()) -> {ok, float()} | {error, nil}.
try_nth_root(X, N) ->
nth_root(X, N).
-file("src/viva_math/scalar.gleam", 278).
?DOC(" Safe natural log: returns `default` for non-positive input.\n").
-spec safe_log(float(), float()) -> float().
safe_log(X, Default) ->
case X =< +0.0 of
true ->
Default;
false ->
math:log(X)
end.
-file("src/viva_math/scalar.gleam", 286).
?DOC(" Safe exp clamped to avoid overflow (exp(709) ≈ max_float).\n").
-spec safe_exp(float()) -> float().
safe_exp(X) ->
case X of
X@1 when X@1 > 700.0 ->
math:exp(700.0);
X@2 when X@2 < -700.0 ->
+0.0;
_ ->
math:exp(X)
end.
-file("src/viva_math/scalar.gleam", 295).
?DOC(" Safe square root: returns 0 for negative input.\n").
-spec safe_sqrt(float()) -> float().
safe_sqrt(X) ->
case X =< +0.0 of
true ->
+0.0;
false ->
math:sqrt(X)
end.
-file("src/viva_math/scalar.gleam", 303).
?DOC(" Safe division.\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/scalar.gleam", 318).
?DOC(
" log(exp(a) + exp(b)) without overflow.\n"
"\n"
" Uses the identity log(e^a + e^b) = max(a,b) + log(1 + exp(-|a-b|)).\n"
" When `a == b` (including ±∞) short-circuits to avoid ∞ - ∞ → NaN.\n"
).
-spec logaddexp(float(), float()) -> float().
logaddexp(A, B) ->
case A =:= B of
true ->
A + math:log(2.0);
false ->
M = gleam@float:max(A, B),
D = gleam@float:absolute_value(A - B),
M + log1p(math:exp(+0.0 - D))
end.
-file("src/viva_math/scalar.gleam", 352).
-spec list_shifted_exps(list(float()), float(), list(float())) -> list(float()).
list_shifted_exps(Xs, M, Acc) ->
case Xs of
[] ->
Acc;
[X | Rest] ->
list_shifted_exps(Rest, M, [math:exp(X - M) | Acc])
end.
-file("src/viva_math/scalar.gleam", 345).
-spec list_max(list(float()), float()) -> float().
list_max(Xs, Acc) ->
case Xs of
[] ->
Acc;
[X | Rest] ->
list_max(Rest, gleam@float:max(Acc, X))
end.
-file("src/viva_math/scalar.gleam", 333).
?DOC(
" log(Σ exp(xᵢ)) — log-sum-exp with max subtraction for stability.\n"
"\n"
" Uses Neumaier compensated summation on the exp-shifted terms to retain\n"
" precision when xᵢ values span many orders of magnitude.\n"
).
-spec logsumexp(list(float())) -> float().
logsumexp(Xs) ->
case Xs of
[] ->
+0.0 - 1.7976931348623157e308;
[X] ->
X;
_ ->
M = list_max(Xs, +0.0 - 1.7976931348623157e308),
Shifted = list_shifted_exps(Xs, M, []),
M + math:log(viva_math@precision:neumaier_sum(Shifted))
end.
-file("src/viva_math/scalar.gleam", 364).
?DOC(" Hypotenuse √(x² + y²) without intermediate overflow.\n").
-spec hypot(float(), float()) -> float().
hypot(X, Y) ->
Ax = gleam@float:absolute_value(X),
Ay = gleam@float:absolute_value(Y),
case {Ax, Ay} of
{+0.0, +0.0} ->
+0.0;
{_, _} ->
M = gleam@float:max(Ax, Ay),
N = gleam@float:min(Ax, Ay),
R = case M of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator -> N / Gleam@denominator
end,
M * math:sqrt(1.0 + (R * R))
end.
-file("src/viva_math/scalar.gleam", 383).
?DOC(" Standard sigmoid σ(x) = 1 / (1 + e^(-x)).\n").
-spec sigmoid(float()) -> float().
sigmoid(X) ->
case X >= +0.0 of
true ->
case (1.0 + math:exp(+0.0 - X)) of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator -> 1.0 / Gleam@denominator
end;
false ->
Ex = math:exp(X),
case (1.0 + Ex) of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator@1 -> Ex / Gleam@denominator@1
end
end.
-file("src/viva_math/scalar.gleam", 395).
?DOC(" Logit / inverse sigmoid: ln(p / (1 - p)). Domain p ∈ (0, 1).\n").
-spec logit(float()) -> float().
logit(P) ->
P_clamped = gleam@float:max(
gleam@float:min(P, 1.0 - 2.220446049250313e-16),
2.220446049250313e-16
),
math:log(case (1.0 - P_clamped) of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator -> P_clamped / Gleam@denominator
end).
-file("src/viva_math/scalar.gleam", 402).
?DOC(" Generalized sigmoid with steepness `k`: σ(kx).\n").
-spec sigmoid_k(float(), float()) -> float().
sigmoid_k(X, K) ->
sigmoid(K * X).
-file("src/viva_math/scalar.gleam", 411).
?DOC(" ReLU: max(0, x).\n").
-spec relu(float()) -> float().
relu(X) ->
gleam@float:max(+0.0, X).
-file("src/viva_math/scalar.gleam", 416).
?DOC(" Leaky ReLU: x if x > 0 else negative_slope · x.\n").
-spec leaky_relu(float(), float()) -> float().
leaky_relu(X, Negative_slope) ->
case X > +0.0 of
true ->
X;
false ->
Negative_slope * X
end.
-file("src/viva_math/scalar.gleam", 424).
?DOC(" ELU: x if x > 0 else α(e^x - 1).\n").
-spec elu(float(), float()) -> float().
elu(X, Alpha) ->
case X > +0.0 of
true ->
X;
false ->
Alpha * expm1(X)
end.
-file("src/viva_math/scalar.gleam", 432).
?DOC(" SELU (self-normalizing). Scale and alpha from Klambauer et al. 2017.\n").
-spec selu(float()) -> float().
selu(X) ->
Scale = 1.0507009873554805,
Alpha = 1.6732632423543772,
Scale * elu(X, Alpha).
-file("src/viva_math/scalar.gleam", 441).
?DOC(
" GELU exact: x · Φ(x) using erf.\n"
"\n"
" Φ(x) = ½ · (1 + erf(x / √2)). Used by BERT/GPT.\n"
).
-spec gelu(float()) -> float().
gelu(X) ->
(0.5 * X) * (1.0 + math:erf(X * 0.7071067811865475)).
-file("src/viva_math/scalar.gleam", 448).
?DOC(
" GELU tanh approximation (Hendrycks & Gimpel).\n"
"\n"
" Faster, used by GPT-2/PaLM. Accurate to ~4 decimals.\n"
).
-spec gelu_approx(float()) -> float().
gelu_approx(X) ->
C = 0.7978845608028654,
Inner = C * (X + (((0.044715 * X) * X) * X)),
(0.5 * X) * (1.0 + math:tanh(Inner)).
-file("src/viva_math/scalar.gleam", 456).
?DOC(" SiLU / Swish: x · σ(x).\n").
-spec silu(float()) -> float().
silu(X) ->
X * sigmoid(X).
-file("src/viva_math/scalar.gleam", 461).
?DOC(" Alias for `silu`. Used by Google's original Swish paper.\n").
-spec swish(float()) -> float().
swish(X) ->
silu(X).
-file("src/viva_math/scalar.gleam", 474).
?DOC(
" Softplus: ln(1 + e^x). Smooth ReLU.\n"
"\n"
" Uses safe identity for large x to avoid overflow:\n"
" softplus(x) = max(x, 0) + log1p(exp(-|x|)).\n"
).
-spec softplus(float()) -> float().
softplus(X) ->
gleam@float:max(X, +0.0) + log1p(
math:exp(+0.0 - gleam@float:absolute_value(X))
).
-file("src/viva_math/scalar.gleam", 466).
?DOC(" Mish: x · tanh(softplus(x)).\n").
-spec mish(float()) -> float().
mish(X) ->
X * math:tanh(softplus(X)).
-file("src/viva_math/scalar.gleam", 490).
?DOC(
" λ-GELU: hardness-parameterised GELU `x · Φ(λx)` with `λ ≥ 1`.\n"
"\n"
" `λ = 1` recovers standard GELU; `λ → ∞` approaches ReLU. Useful for\n"
" learnable activation hardness, hardware quantisation-friendly fine-tuning,\n"
" and gradual ReLU substitution during training.\n"
"\n"
" Reference: Cantos & Aragón (2026) \"λ-GELU: A Hardness-Parameterised GELU\n"
" for Smooth ReLU Substitution\" (arXiv:2603.21991).\n"
).
-spec lambda_gelu(float(), float()) -> float().
lambda_gelu(X, Lambda) ->
L = case Lambda < 1.0 of
true ->
1.0;
false ->
Lambda
end,
(0.5 * X) * (1.0 + math:erf((L * X) * 0.7071067811865475)).
-file("src/viva_math/scalar.gleam", 536).
?DOC(" Single-argument arctangent. Delegates to `:math.atan/1`.\n").
-spec atan(float()) -> float().
atan(X) ->
math:atan(X).
-file("src/viva_math/scalar.gleam", 538).
-spec inv_pi() -> float().
inv_pi() ->
0.3183098861837907.
-file("src/viva_math/scalar.gleam", 506).
?DOC(
" IGLU (Integrated Gaussian Linear Unit): continuous mixture of GELU\n"
" activations under a Gaussian dispersion of \"decision hardness\".\n"
"\n"
" `IGLU(x; σ) = x · Z(x; σ)` where Z uses arctan-based gating. Provides\n"
" smoother gradients than GELU near zero and heavier tails.\n"
"\n"
" Reference: Aragón et al. (2026) \"IGLU: An Integrated Gaussian Linear\n"
" Activation Function\" (arXiv:2603.06861).\n"
).
-spec iglu(float(), float()) -> float().
iglu(X, Sigma) ->
S = case Sigma =< +0.0 of
true ->
1.0e-6;
false ->
Sigma
end,
Gate = 0.5 + (inv_pi() * math:atan(case S of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator -> X / Gleam@denominator
end)),
X * Gate.
-file("src/viva_math/scalar.gleam", 520).
?DOC(
" IGLU rational approximation — same qualitative behaviour as `iglu`\n"
" without transcendental functions. Maximum deviation ~5 %.\n"
"\n"
" Recommended when execution speed matters more than exact GELU shape\n"
" (e.g. low-precision quantised inference).\n"
).
-spec iglu_approx(float(), float()) -> float().
iglu_approx(X, Sigma) ->
S = case Sigma =< +0.0 of
true ->
1.0e-6;
false ->
Sigma
end,
T = case S of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator -> X / Gleam@denominator
end,
Denom = 1.0 + ((T * T) / 3.0),
Gate = 0.5 + (case Denom of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator@1 -> inv_pi() * T / Gleam@denominator@1
end),
X * Gate.
-file("src/viva_math/scalar.gleam", 545).
?DOC(
" Hard sigmoid: piecewise linear approximation of sigmoid.\n"
"\n"
" Returns 0 for x ≤ -3, 1 for x ≥ 3, linear interpolation between.\n"
).
-spec hard_sigmoid(float()) -> float().
hard_sigmoid(X) ->
case X of
X@1 when X@1 =< -3.0 ->
+0.0;
X@2 when X@2 >= 3.0 ->
1.0;
_ ->
(X / 6.0) + 0.5
end.
-file("src/viva_math/scalar.gleam", 554).
?DOC(" Hard swish: x · hard_sigmoid(x). Used in MobileNetV3.\n").
-spec hard_swish(float()) -> float().
hard_swish(X) ->
X * hard_sigmoid(X).
-file("src/viva_math/scalar.gleam", 559).
?DOC(" Hard tanh: clamps to [-1, 1].\n").
-spec hard_tanh(float()) -> float().
hard_tanh(X) ->
gleam@float:max(-1.0, gleam@float:min(1.0, X)).
-file("src/viva_math/scalar.gleam", 568).
?DOC(" Clamp to range [min, max].\n").
-spec clamp(float(), float(), float()) -> float().
clamp(X, Min, Max) ->
gleam@float:max(Min, gleam@float:min(Max, X)).
-file("src/viva_math/scalar.gleam", 573).
?DOC(" Clamp to unit [0, 1].\n").
-spec clamp_unit(float()) -> float().
clamp_unit(X) ->
clamp(X, +0.0, 1.0).
-file("src/viva_math/scalar.gleam", 578).
?DOC(" Clamp to bipolar [-1, 1].\n").
-spec clamp_bipolar(float()) -> float().
clamp_bipolar(X) ->
clamp(X, -1.0, 1.0).
-file("src/viva_math/scalar.gleam", 583).
?DOC(" Linear interpolation between a and b.\n").
-spec lerp(float(), float(), float()) -> float().
lerp(A, B, T) ->
A + (T * (B - A)).
-file("src/viva_math/scalar.gleam", 590).
?DOC(
" Smoothstep: cubic Hermite interpolation between edge0 and edge1.\n"
"\n"
" When the edges coincide, behaves as a step at `edge0`.\n"
).
-spec smoothstep(float(), float(), float()) -> float().
smoothstep(Edge0, Edge1, X) ->
case Edge0 =:= Edge1 of
true ->
case X < Edge0 of
true ->
+0.0;
false ->
1.0
end;
false ->
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))
end.
-file("src/viva_math/scalar.gleam", 605).
?DOC(" Smootherstep: quintic version (zero first and second derivatives at edges).\n").
-spec smootherstep(float(), float(), float()) -> float().
smootherstep(Edge0, Edge1, X) ->
case Edge0 =:= Edge1 of
true ->
case X < Edge0 of
true ->
+0.0;
false ->
1.0
end;
false ->
T = clamp_unit(case (Edge1 - Edge0) of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator -> (X - Edge0) / Gleam@denominator
end),
((T * T) * T) * ((T * ((T * 6.0) - 15.0)) + 10.0)
end.
-file("src/viva_math/scalar.gleam", 624).
?DOC(" Sign function: -1, 0, or 1.\n").
-spec sign(float()) -> float().
sign(X) ->
case X of
X@1 when X@1 > +0.0 ->
1.0;
X@2 when X@2 < +0.0 ->
-1.0;
_ ->
+0.0
end.
-file("src/viva_math/scalar.gleam", 633).
?DOC(" Step function: 0 if x < threshold, 1 otherwise.\n").
-spec step(float(), float()) -> float().
step(Threshold, X) ->
case X < Threshold of
true ->
+0.0;
false ->
1.0
end.
-file("src/viva_math/scalar.gleam", 641).
?DOC(" Convert degrees to radians.\n").
-spec deg_to_rad(float()) -> float().
deg_to_rad(Deg) ->
Deg * 0.017453292519943295.
-file("src/viva_math/scalar.gleam", 646).
?DOC(" Convert radians to degrees.\n").
-spec rad_to_deg(float()) -> float().
rad_to_deg(Rad) ->
Rad * 57.29577951308232.