viva_math/scalar
Scalar mathematical functions.
This module provides pipeline-friendly scalar primitives that are either
missing from gleam_community_maths or commonly needed for ML / scientific
computing. Where the Erlang stdlib already exposes a fast BIF (such as
:math.erf/1, :math.erfc/1 or :math.fmod/2), we delegate through FFI
rather than reimplementing.
Design rules
- All functions take and return
Floatdirectly (noResult) unless a genuine domain error is unavoidable. - Numerically stable variants are preferred:
logsumexp,softplusandsafe_log/exp/sqrtguard against overflow / NaN. - Activations follow the conventions used by
viva_tensor,pytorchandjaxso thatviva_tensorcan delegate scalar paths here directly.
Erlang BIFs used (OTP 22+, confirmed on OTP 28)
:math.erf/1, :math.erfc/1, :math.fmod/2, :math.expm1/1,
:math.log1p/1, :math.tanh/1, :math.exp/1, :math.log/1,
:math.sqrt/1, :math.pow/2.
Values
pub fn erf(x: Float) -> Float
Error function erf(x) = (2/√π) · ∫₀ˣ e^(-t²) dt.
Delegates to :math.erf/1 (Erlang stdlib BIF).
pub fn erfc(x: Float) -> Float
Complementary error function erfc(x) = 1 - erf(x).
Computed by methods that avoid cancellation for large x.
Delegates to :math.erfc/1.
pub fn expm1(x: Float) -> Float
exp(x) - 1 accurate for small x.
Uses a 4-term Maclaurin series near zero (|x| < 1e-5) to avoid the
catastrophic cancellation of exp(x) - 1.0. Falls back to the direct
expression elsewhere. (Not all OTP builds ship :math.expm1/1, so we
implement it portably.)
pub fn fmod(x: Float, y: Float) -> Float
Floating-point remainder x mod y (IEEE 754 fmod).
Delegates to :math.fmod/2.
pub fn gelu(x: Float) -> Float
GELU exact: x · Φ(x) using erf.
Φ(x) = ½ · (1 + erf(x / √2)). Used by BERT/GPT.
pub fn gelu_approx(x: Float) -> Float
GELU tanh approximation (Hendrycks & Gimpel).
Faster, used by GPT-2/PaLM. Accurate to ~4 decimals.
pub fn hard_sigmoid(x: Float) -> Float
Hard sigmoid: piecewise linear approximation of sigmoid.
Returns 0 for x ≤ -3, 1 for x ≥ 3, linear interpolation between.
pub fn hard_swish(x: Float) -> Float
Hard swish: x · hard_sigmoid(x). Used in MobileNetV3.
pub fn hypot(x: Float, y: Float) -> Float
Hypotenuse √(x² + y²) without intermediate overflow.
pub fn iglu(x: Float, sigma: Float) -> Float
IGLU (Integrated Gaussian Linear Unit): continuous mixture of GELU activations under a Gaussian dispersion of “decision hardness”.
IGLU(x; σ) = x · Z(x; σ) where Z uses arctan-based gating. Provides
smoother gradients than GELU near zero and heavier tails.
Reference: Aragón et al. (2026) “IGLU: An Integrated Gaussian Linear Activation Function” (arXiv:2603.06861).
pub fn iglu_approx(x: Float, sigma: Float) -> Float
IGLU rational approximation — same qualitative behaviour as iglu
without transcendental functions. Maximum deviation ~5 %.
Recommended when execution speed matters more than exact GELU shape (e.g. low-precision quantised inference).
pub fn lambda_gelu(x: Float, lambda: Float) -> Float
λ-GELU: hardness-parameterised GELU x · Φ(λx) with λ ≥ 1.
λ = 1 recovers standard GELU; λ → ∞ approaches ReLU. Useful for
learnable activation hardness, hardware quantisation-friendly fine-tuning,
and gradual ReLU substitution during training.
Reference: Cantos & Aragón (2026) “λ-GELU: A Hardness-Parameterised GELU for Smooth ReLU Substitution” (arXiv:2603.21991).
pub fn leaky_relu(x: Float, negative_slope: Float) -> Float
Leaky ReLU: x if x > 0 else negative_slope · x.
pub fn log1p(x: Float) -> Float
log(1 + x) accurate for small x.
Uses a Maclaurin series near zero to avoid cancellation in ln(1.0 + x).
Falls back to direct logarithm elsewhere.
pub fn logaddexp(a: Float, b: Float) -> Float
log(exp(a) + exp(b)) without overflow.
Uses the identity log(e^a + e^b) = max(a,b) + log(1 + exp(-|a-b|)).
When a == b (including ±∞) short-circuits to avoid ∞ - ∞ → NaN.
pub fn logit(p: Float) -> Float
Logit / inverse sigmoid: ln(p / (1 - p)). Domain p ∈ (0, 1).
pub fn logsumexp(xs: List(Float)) -> Float
log(Σ exp(xᵢ)) — log-sum-exp with max subtraction for stability.
Uses Neumaier compensated summation on the exp-shifted terms to retain precision when xᵢ values span many orders of magnitude.
pub fn safe_exp(x: Float) -> Float
Safe exp clamped to avoid overflow (exp(709) ≈ max_float).
pub fn safe_log(x: Float, default: Float) -> Float
Safe natural log: returns default for non-positive input.
pub fn selu(x: Float) -> Float
SELU (self-normalizing). Scale and alpha from Klambauer et al. 2017.
pub fn sigmoid_k(x: Float, k: Float) -> Float
Generalized sigmoid with steepness k: σ(kx).
pub fn smootherstep(
edge0: Float,
edge1: Float,
x: Float,
) -> Float
Smootherstep: quintic version (zero first and second derivatives at edges).
pub fn smoothstep(edge0: Float, edge1: Float, x: Float) -> Float
Smoothstep: cubic Hermite interpolation between edge0 and edge1.
When the edges coincide, behaves as a step at edge0.
pub fn softplus(x: Float) -> Float
Softplus: ln(1 + e^x). Smooth ReLU.
Uses safe identity for large x to avoid overflow: softplus(x) = max(x, 0) + log1p(exp(-|x|)).
pub fn step(threshold: Float, x: Float) -> Float
Step function: 0 if x < threshold, 1 otherwise.