v1 0.0.1

Scalar mathematical library for neural networks.
Documentation
// libs/v1/src/ml/loss.rs
//
//! # Функции потерь (Loss Functions) и их градиенты.
//!
//! Модуль содержит скалярные математические функции для оценки ошибки
//! предсказания нейрона относительно целевого значения (target), а также
//! первые производные этих функций для вычисления градиентов.

// libs/v1/src/ml/loss.rs
//
//! # Функции потерь (Loss Functions) и их градиенты.
//!
//! Модуль содержит скалярные математические функции для оценки ошибки
//! предсказания нейрона относительно целевого значения (target), а также
//! первые производные этих функций для вычисления градиентов в процессе
//! обратного распространения ошибки (backpropagation).

/// # Среднеквадратичная ошибка (MSE / Mean Squared Error) для скаляра
///
/// Вычисляет квадрат разности между предсказанием и целью. Сильно штрафует
/// модель за крупные ошибки (выбросы), так как значение функции растет квадратично.
///
/// ### График поведения функции:
/// ```text
///      Loss
///       ^      .
///       |     /
///       |    /  
///       |   .   
///       |__.__.__>  Error (pred - target)
/// ```
///
/// ### Формула
/// ```text
/// MSE(pred, target) = (pred - target)²
/// ```
///
/// ### Примеры
/// ```
/// use v1::ml::loss::mse;
///
/// // При точном совпадении ошибка равна нулю
/// assert_eq!(mse(5.0, 5.0), 0.0);
/// // Квадратичный штраф за отклонение: (2.0 - 5.0)^2 = (-3.0)^2 = 9.0
/// assert!((mse(2.0, 5.0) - 9.0).abs() < 1e-6);
/// ```
pub fn mse(pred: f32, target: f32) -> f32 {
    let diff = pred - target;
    diff * diff
}

/// # Градиент функции MSE
///
/// Первая производная скалярной функции MSE по переменной `pred`.
/// Направление и величина градиента линейно зависят от величины ошибки,
/// что обеспечивает быстрое сваливание к минимуму при больших отклонениях.
///
/// ### Формула
/// ```text
/// d/d(pred) MSE = 2 · (pred - target)
/// ```
///
/// ### Примеры
/// ```
/// use v1::ml::loss::mse_gradient;
///
/// // Если предсказание больше цели, градиент положителен
/// assert_eq!(mse_gradient(5.0, 2.0), 6.0);
/// // Если предсказание меньше цели, градиент отрицателен
/// assert_eq!(mse_gradient(1.0, 4.0), -6.0);
/// ```
pub fn mse_gradient(pred: f32, target: f32) -> f32 {
    2.0 * (pred - target)
}

/// # Абсолютная ошибка (MAE / Mean Absolute Error) для скаляра
///
/// Вычисляет модуль разности между предсказанием и целью. Устойчива к выбросам
/// в данных, так как штраф растет строго линейно, а не квадратично.
///
/// ### График поведения функции:
/// ```text
///      Loss
///       ^     /
///       |    / 
///       |   /  
///       |  /   
///       |__.__.__>  Error (pred - target)
/// ```
///
/// ### Формула
/// ```text
/// MAE(pred, target) = |pred - target|
/// ```
///
/// ### Примеры
/// ```
/// use v1::ml::loss::mae;
///
/// assert_eq!(mae(10.0, 4.0), 6.0);
/// assert_eq!(mae(4.0, 10.0), 6.0);
/// ```
pub fn mae(pred: f32, target: f32) -> f32 {
    (pred - target).abs()
}

/// # Градиент функции MAE
///
/// Первая производная скалярной функции MAE по переменной `pred`.
/// Возвращает шаг постоянного знака. Математически производная модуля не определена
/// в точке излома (когда `pred == target`). Данная лоу-левел реализация стабилизирует
/// эту точку, возвращая `0.0`.
///
/// ### Формула
/// ```text
/// d/d(pred) MAE = sign(pred - target)
/// ```
///
/// ### Примеры
/// ```
/// use v1::ml::loss::mae_gradient;
///
/// // Градиент постоянен по модулю и зависит только от знака ошибки
/// assert_eq!(mae_gradient(100.0, 50.0), 1.0);
/// assert_eq!(mae_gradient(1.0, 5.0), -1.0);
/// // Точка излома (равенство аргументов) безопасно гасится в 0.0
/// assert_eq!(mae_gradient(3.5, 3.5), 0.0);
/// ```
pub fn mae_gradient(pred: f32, target: f32) -> f32 {
    match pred - target {
        v if v > 0.0 => 1.0,
        v if v < 0.0 => -1.0,
        _ => 0.0,
    }
}

/// # Функция потерь Хубера (Huber Loss)
///
/// Гибридная функция потерь, сочетающая свойства MSE и MAE. Работает как 
/// квадратичная ошибка (MSE), когда разность меньше порогового значения `delta`,
/// и плавно переключается на линейную (MAE), когда разность велика. 
/// Это защищает глубокие сети от взрыва градиентов при сильных аномалиях в данных.
///
/// ### График поведения функции:
/// ```text
///      Loss
///       ^     /  (Линейная зона MAE при |err| > delta)
///       |    .   
///       |   (    (Квадратичная зона MSE при |err| <= delta)
///       |  .     
///       |__.__.__>  Error (pred - target)
/// ```
///
/// ### Формула
/// ```text
/// При |pred - target| <= delta: 0.5 · (pred - target)²
/// При |pred - target| > delta:  delta · (|pred - target| - 0.5 · delta)
/// ```
///
/// ### Примеры
/// ```
/// use v1::ml::loss::huber;
///
/// // Внутри параболической зоны (diff = 1.0 <= delta = 2.0): 0.5 * 1.0^2 = 0.5
/// assert!((huber(2.0, 1.0, 2.0) - 0.5).abs() < 1e-6);
/// // Вне параболической зоны (diff = 5.0 > delta = 1.0): 1.0 * (5.0 - 0.5 * 1.0) = 4.5
/// assert!((huber(5.0, 0.0, 1.0) - 4.5).abs() < 1e-6);
/// ```
pub fn huber(pred: f32, target: f32, delta: f32) -> f32 {
    let diff = pred - target;
    let abs_diff = diff.abs();

    if abs_diff <= delta {
        0.5 * diff * diff
    } else {
        delta * (abs_diff - 0.5 * delta)
    }
}

/// # Градиент функции потерь Хубера
///
/// Первая производная функции Huber Loss по переменной `pred`.
/// Обеспечивает автоматическое ограничение (clipping) максимального значения
/// градиента величиной порога `delta`, предотвращая дестабилизацию шага обучения.
///
/// ### Формула
/// ```text
/// При |pred - target| <= delta: pred - target
/// При |pred - target| > delta:  delta · sign(pred - target)
/// ```
///
/// ### Примеры
/// ```
/// use v1::ml::loss::huber_gradient;
///
/// // Внутри зоны градиент равен чистой разности аргументов
/// assert_eq!(huber_gradient(3.0, 2.0, 2.0), 1.0);
/// // Вне зоны градиент жестко лимитируется (клипается) значением delta
/// assert_eq!(huber_gradient(10.0, 0.0, 1.5), 1.5);
/// assert_eq!(huber_gradient(0.0, 10.0, 1.5), -1.5);
/// ```
pub fn huber_gradient(pred: f32, target: f32, delta: f32) -> f32 {
    let diff = pred - target;
    let abs_diff = diff.abs();

    if abs_diff <= delta {
        diff
    } else {
        delta * diff.signum()
    }
}