optirs_core/regularizers/
l2.rs

1// L2 (Ridge) regularization
2
3use scirs2_core::ndarray::{Array, Dimension, ScalarOperand, Zip};
4use scirs2_core::numeric::Float;
5use std::fmt::Debug;
6
7use crate::error::Result;
8use crate::regularizers::Regularizer;
9
10/// L2 (Ridge) regularization
11///
12/// Adds a penalty equal to the sum of the squared values of the parameters,
13/// which encourages smaller weights and penalizes large weights.
14///
15/// Penalty: 0.5 * alpha * sum(params^2)
16///
17/// # Examples
18///
19/// ```
20/// use scirs2_core::ndarray::Array1;
21/// use optirs_core::regularizers::{L2, Regularizer};
22///
23/// // Create an L2 regularizer with strength 0.01
24/// let regularizer = L2::new(0.01);
25///
26/// // Parameters and gradients
27/// let params = Array1::from_vec(vec![0.5, -0.3, 0.0, 0.2]);
28/// let mut gradients = Array1::from_vec(vec![0.1, 0.2, -0.3, 0.0]);
29///
30/// // Apply regularization
31/// let penalty = regularizer.apply(&params, &mut gradients).unwrap();
32///
33/// // Gradients will be modified to include the L2 penalty gradient
34/// // Penalty will be: 0.5 * 0.01 * (0.5^2 + (-0.3)^2 + 0.0^2 + 0.2^2) = 0.005 * 0.38 = 0.0019
35/// ```
36#[derive(Debug, Clone, Copy)]
37pub struct L2<A: Float + Debug> {
38    /// Regularization strength
39    alpha: A,
40}
41
42impl<A: Float + Debug + Send + Sync> L2<A> {
43    /// Create a new L2 regularizer
44    ///
45    /// # Arguments
46    ///
47    /// * `alpha` - Regularization strength
48    pub fn new(alpha: A) -> Self {
49        Self { alpha }
50    }
51
52    /// Get the regularization strength
53    pub fn alpha(&self) -> A {
54        self.alpha
55    }
56
57    /// Set the regularization strength
58    pub fn set_alpha(&mut self, alpha: A) -> &mut Self {
59        self.alpha = alpha;
60        self
61    }
62}
63
64impl<A, D> Regularizer<A, D> for L2<A>
65where
66    A: Float + ScalarOperand + Debug,
67    D: Dimension,
68{
69    fn apply(&self, params: &Array<A, D>, gradients: &mut Array<A, D>) -> Result<A> {
70        // L2 gradient: alpha * params
71        Zip::from(params).and(gradients).for_each(|&param, grad| {
72            *grad = *grad + self.alpha * param;
73        });
74
75        self.penalty(params)
76    }
77
78    fn penalty(&self, params: &Array<A, D>) -> Result<A> {
79        // L2 penalty: 0.5 * alpha * sum(params^2)
80        let half = A::from(0.5).unwrap();
81        let sum_squared = params.iter().fold(A::zero(), |acc, &x| acc + x * x);
82        Ok(half * self.alpha * sum_squared)
83    }
84}