viva_math/scalar
Scalar mathematical functions.
This module provides pipeline-friendly scalar primitives 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 acos(x: Float) -> Float
Inverse cosine. Domain x ∈ [-1, 1]; outside crashes (BIF behaviour).
pub fn asin(x: Float) -> Float
Inverse sine. Domain x ∈ [-1, 1]; outside crashes (BIF behaviour).
pub fn cube_root(x: Float) -> Result(Float, Nil)
Real cube root with Result API for parity with square_root.
Total over Float, so Ok always; kept for ergonomic chaining.
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 logarithm(x: Float) -> Result(Float, Nil)
Natural logarithm with domain check. Returns Error(Nil) for x ≤ 0.
pub fn logarithm_10(x: Float) -> Result(Float, Nil)
Log base 10 with domain check. Returns Error(Nil) for x ≤ 0.
pub fn logarithm_2(x: Float) -> Result(Float, Nil)
Log base 2 with domain check. Returns Error(Nil) for x ≤ 0.
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 nth_root(x: Float, n: Int) -> Result(Float, Nil)
Generalised real n-th root (x^(1/n)) for integer n ≥ 1.
n = 1→ returnsOk(x).n = 2→ usessquare_root(errors forx < 0).- Odd
n→ defined for all realx(sign trick). - Even
n > 2→ errors forx < 0; usespow(x, 1/n)otherwise. n ≤ 0→Error(Nil).
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 square_root(x: Float) -> Result(Float, Nil)
Square root with domain check. Returns Error(Nil) for x < 0.
pub fn step(threshold: Float, x: Float) -> Float
Step function: 0 if x < threshold, 1 otherwise.
pub fn try_cbrt(x: Float) -> Result(Float, Nil)
Deprecated: Use `scalar.cube_root` instead.
Deprecated alias for cube_root.
pub fn try_ln(x: Float) -> Result(Float, Nil)
Deprecated: Use `scalar.logarithm` instead.
Deprecated alias for logarithm.
pub fn try_log10(x: Float) -> Result(Float, Nil)
Deprecated: Use `scalar.logarithm_10` instead.
Deprecated alias for logarithm_10.
pub fn try_log2(x: Float) -> Result(Float, Nil)
Deprecated: Use `scalar.logarithm_2` instead.
Deprecated alias for logarithm_2.
pub fn try_nth_root(x: Float, n: Int) -> Result(Float, Nil)
Deprecated: Use `scalar.nth_root` instead.
Deprecated alias for nth_root.