v1 0.0.1

Scalar mathematical library for neural networks.
Documentation
// libs/v1/src/ml/activation.rs
//
//! # Функции для машинного обучения (ML).
//! 
//! Модуль содержит популярные функции активации, используемые при построении
//! нейронных сетей, такие как GELU и ReLU.
//! 
//! **Примечание**: rustdoc не рендерит `LaTeX`, поэтому приходится использовать unicode, и другие символы для показа формул.
use crate::constants::SQRT_2_OVER_PI;

/// # Аппроксимация GELU через tanh
///
/// **GELU** (Gaussian Error Linear Unit) — это плавная функция активации, которая взвешивает
/// входное значение по его вероятности в стандартном нормальном распределении. Часто применяется
/// в современных архитектурах вроде Transformer (BERT, GPT).
///
/// Данная реализация использует быструю и точную аппроксимацию через гиперболический тангенс.
///
/// ### Формула
/// ```text
/// GELU(x) ≈ 0.5x · (1 + tanh(√(2/π) · (x + 0.044715x³)))
/// ```
///
/// ### Примеры
/// ```
/// use v1::ml::activation::gelu_tanh;
///
/// assert!((gelu_tanh(0.0) - 0.0).abs() < 1e-6);
/// assert!((gelu_tanh(1.0) - 0.8413).abs() < 1e-3);
/// assert!(gelu_tanh(-4.0).abs() < 1e-3);
/// ```
pub fn gelu_tanh(x: f32) -> f32 {
    0.5 * x * (1.0 + (SQRT_2_OVER_PI * (x + 0.044715 * x.powi(3))).tanh())
}

/// # Аппроксимация GELU через сигмоиду
///
/// Более быстрая, но чуть менее точная (по сравнению с tanh) аппроксимация функции GELU.
/// Она вычисляется через масштабированную сигмоидальную функцию. Часто используется в задачах,
/// где критична максимальная скорость вычислений в ущерб минимальной точности на изгибах.
///
/// ### Формула
/// ```text
///                                 x
/// GELU(x) ≈ x * σ(1.702 * x) = ———————
///                              1 + e⁻¹·⁷⁰²ˣ
/// ```
/// 
/// ### Примеры
/// ```
/// use v1::ml::activation::gelu_sigmoid;
///
/// assert!((gelu_sigmoid(0.0) - 0.0).abs() < 1e-6);
/// assert!((gelu_sigmoid(1.0) - 0.8458).abs() < 1e-2);
/// assert!(gelu_sigmoid(-4.0).abs() < 1e-2);
/// ```
pub fn gelu_sigmoid(x: f32) -> f32 {
    x / (1.0 + (-1.702 * x).exp())
}

/// # Функция активации ReLU (Rectified Linear Unit)
///
/// **ReLU** — одна из самых популярных и вычислительно эффективных функций активации.
/// Она возвращает `x`, если число положительное, и `0.0`, если отрицательное или ноль.
///
/// Эффективно решает проблему затухания градиентов в глубоких сетях, однако подвержена
/// проблеме "умирающих нейронов" (Dying ReLU), когда нейроны на отрицательных диапазонах
/// перестают обновлять свои веса.
///
/// ### Формула
/// ```text
/// ReLU(x) = max(0, x)
/// ```
///
/// ### Примеры
/// ```
/// use v1::ml::activation::relu;
///
/// assert_eq!(relu(5.0), 5.0);
/// assert_eq!(relu(-3.0), 0.0);
/// assert_eq!(relu(-0.0), 0.0);
/// ```
pub fn relu(x: f32) -> f32 {
    x.max(0.0)
}

/// # Логистическая функция (Сигмоида / Sigmoid)
///
/// Преобразует любое входное значение в диапазон от `0.0` до `1.0`. 
/// Часто используется на выходном слое нейросети для предсказания вероятностей.
///
/// ### Формула
/// ```text
///              1
/// σ(x) = —————————————
///         1 + e⁻ˣ
/// ```
///
/// ### Примеры
/// ```
/// use v1::ml::activation::sigmoid;
///
/// assert!((sigmoid(0.0) - 0.5).abs() < 1e-6);
/// assert!((sigmoid(2.0) - 0.880797).abs() < 1e-5);
/// assert!(sigmoid(-10.0) < 1e-4);
/// ```
pub fn sigmoid(x: f32) -> f32 {
    1.0 / (1.0 + (-x).exp())
}

/// # Функция активации LeakyReLU
///
/// Модификация классической `ReLU`. Вместо полного обнуления отрицательных значений,
/// она пропускает их с небольшим коэффициентом (альфа), что предотвращает проблему
/// "умирающих нейронов" (Dying ReLU).
///
/// ### Формула
/// ```text
/// LeakyReLU(x) = max(α * x, x)
/// 
/// где α (alpha) обычно равен 0.01
/// ```
///
/// ### Примеры
/// ```
/// use v1::ml::activation::leaky_relu;
///
/// assert_eq!(leaky_relu(5.0, 0.01), 5.0);
/// assert_eq!(leaky_relu(-2.0, 0.01), -0.02);
/// ```
pub fn leaky_relu(x: f32, alpha: f32) -> f32 {
    if x > 0.0 { x } else { alpha * x }
}

/// # Функция активации Softplus
///
/// Гладкая и непрерывная альтернатива функции `ReLU`. Преобразует любое 
/// входное значение в строго положительное число в диапазоне `(0.0, +inf)`.
///
/// Первая производная этой функции математически равна стандартной Сигмоиде.
///
/// Для предотвращения численного переполнения (`f32::INFINITY`) при больших
/// значениях `x` применяется линейная стабилизация.
///
/// ### Формула
/// ```text
/// Softplus(x) = ln(1 + eˣ)
/// 
/// При x > 20.0: Softplus(x) ≈ x
/// ```
///
/// ### Примеры
/// ```
/// use v1::ml::activation::softplus;
///
/// // В нуле Softplus равен ln(2) ≈ 0.693147
/// assert!((softplus(0.0) - 0.693147).abs() < 1e-6);
/// // Проверка стабильности при экстремально больших x (не вызывает overflow)
/// assert_eq!(softplus(90.0), 90.0);
/// // Для больших отрицательных x из-за лимитов точности f32 значение округляется до 0.0
/// assert_eq!(softplus(-20.0), 0.0);
/// assert!(softplus(-20.0) <= 1e-5);
/// ```
pub fn softplus(x: f32) -> f32 {
    // Порог 20.0 выбран потому, что e^20 уже обеспечивает точность,
    // превышающую разрядность мантиссы f32.
    if x > 20.0 {
        x
    } else {
        (1.0 + x.exp()).ln()
    }
}

/// # Функция активации SiLU (Swish)
///
/// **SiLU** (Sigmoid Linear Unit), также известная как **Swish**, взвешивает входное
/// значение по его сигмоидальной вероятности. Является стандартом де-факто в 
/// современных архитектурах языковых моделей (LLaMA, Mistral).
///
/// ### Формула
/// ```text
/// SiLU(x) = x · σ(x) = x / (1 + e⁻ˣ)
/// ```
///
/// ### Примеры
/// ```
/// use v1::ml::activation::silu;
///
/// // В нуле SiLU равен строго 0.0
/// assert!((silu(0.0) - 0.0).abs() < 1e-6);
/// // В точке 1.0 значение ≈ 0.731058
/// assert!((silu(1.0) - 0.731058).abs() < 1e-5);
/// ```
pub fn silu(x: f32) -> f32 {
    x * sigmoid(x)
}

/// # Функция активации ELU (Exponential Linear Unit)
///
/// Экспоненциальная линейная единица. Сглаживает отрицательную полуплоскость 
/// с помощью экспоненты, что позволяет среднему значению активаций быть ближе к нулю 
/// и ускоряет сходимость сети при обучении.
///
/// ### Формула
/// ```text
/// ELU(x) = x,             при x > 0
/// ELU(x) = α · (eˣ - 1),  при x ≤ 0
/// ```
///
/// ### Примеры
/// ```
/// use v1::ml::activation::elu;
///
/// assert_eq!(elu(5.0, 1.0), 5.0);
/// // При x <= 0 и alpha = 1.0, в точке -1.0 значение ≈ -0.63212
/// assert!((elu(-1.0, 1.0) - (-0.632120)).abs() < 1e-5);
/// ```
pub fn elu(x: f32, alpha: f32) -> f32 {
    if x > 0.0 {
        x
    } else {
        alpha * (x.exp() - 1.0)
    }
}