rust_ml/core/activations/
sigmoid.rs1use crate::core::activations::activation::Activation;
2use ndarray::{Array, Array1, Array2, Ix1, Ix2};
3
4pub struct Sigmoid;
5
6impl Activation<Ix1> for Sigmoid {
7 fn activate(z: &Array1<f64>) -> Array1<f64> {
8 1.0 / (1.0 + (-z).exp())
9 }
10
11 fn derivative(z: &Array1<f64>) -> Array1<f64> {
12 let y_hat = Self::activate(z);
13 &y_hat * (1.0 - &y_hat)
14 }
15}
16
17impl Activation<Ix2> for Sigmoid {
18 fn activate(z: &Array2<f64>) -> Array2<f64> {
19 1.0 / (1.0 + (-z).exp())
20 }
21
22 fn derivative(z: &Array<f64, Ix2>) -> Array<f64, Ix2> {
23 let y_hat = Self::activate(z);
24 &y_hat * (1.0 - &y_hat)
25 }
26}
27
28#[cfg(test)]
29mod tests {
30 use super::*;
31 use approx::assert_abs_diff_eq;
32 use ndarray::{arr1, arr2};
33 use ndarray_rand::rand_distr::num_traits::Float;
34
35 #[test]
36 fn test_sigmoid_activate_vector() {
37 let input = arr1(&[-2.0, -1.0, 0.0, 1.0, 2.0]);
39 let result = Sigmoid::activate(&input);
40
41 let expected = arr1(&[
43 0.11920292, 0.26894142, 0.5, 0.73105858, 0.88079708, ]);
49
50 assert_eq!(result.len(), expected.len());
51
52 for (a, b) in result.iter().zip(expected.iter()) {
54 assert_abs_diff_eq!(a, b, epsilon = 1e-8);
55 }
56 }
57
58 #[test]
59 fn test_sigmoid_derivative_vector() {
60 let input = arr1(&[-2.0, -1.0, 0.0, 1.0, 2.0]);
62 let result = Sigmoid::derivative(&input);
63
64 let sigmoid_values = arr1(&[
66 0.11920292, 0.26894142, 0.5, 0.73105858, 0.88079708, ]);
72
73 let expected = sigmoid_values.mapv(|v| v * (1.0 - v));
74
75 assert_eq!(result.len(), expected.len());
76
77 for (a, b) in result.iter().zip(expected.iter()) {
79 assert_abs_diff_eq!(a, b, epsilon = 1e-8);
80 }
81 }
82
83 #[test]
84 fn test_sigmoid_activate_matrix() {
85 let input = arr2(&[[-2.0, -1.0, 0.0], [1.0, 2.0, 3.0]]);
87
88 let result = Sigmoid::activate(&input);
89
90 let expected = arr2(&[
92 [0.11920292, 0.26894142, 0.5],
93 [0.73105858, 0.88079708, 0.95257413],
94 ]);
95
96 assert_eq!(result.shape(), expected.shape());
97
98 for ((i, j), value) in result.indexed_iter() {
100 assert_abs_diff_eq!(value, &expected[[i, j]], epsilon = 1e-8);
101 }
102 }
103
104 #[test]
105 fn test_sigmoid_derivative_matrix() {
106 let input = arr2(&[[-2.0, -1.0, 0.0], [1.0, 2.0, 3.0]]);
108
109 let result = Sigmoid::derivative(&input);
110
111 let sigmoid_values = arr2(&[
113 [0.11920292, 0.26894142, 0.5],
114 [0.73105858, 0.88079708, 0.95257413],
115 ]);
116
117 let expected = sigmoid_values.mapv(|v| v * (1.0 - v));
118
119 assert_eq!(result.shape(), expected.shape());
120
121 for ((i, j), value) in result.indexed_iter() {
123 assert_abs_diff_eq!(value, &expected[[i, j]], epsilon = 1e-8);
124 }
125 }
126
127 #[test]
128 fn test_sigmoid_properties() {
129 let zero = arr1(&[0.0]);
131 assert_abs_diff_eq!(Sigmoid::activate(&zero)[0], 0.5, epsilon = 1e-8);
132
133 let x = arr1(&[1.0, 2.0, 3.0]);
135 let neg_x = arr1(&[-1.0, -2.0, -3.0]);
136
137 let sigmoid_x = Sigmoid::activate(&x);
138 let sigmoid_neg_x = Sigmoid::activate(&neg_x);
139
140 for (i, value) in sigmoid_neg_x.iter().enumerate() {
141 assert_abs_diff_eq!(value, &(1.0 - sigmoid_x[i]), epsilon = 1e-8);
142 }
143
144 let points = arr1(&[-2.0, -1.0, -0.5, 0.0, 0.5, 1.0, 2.0]);
146 let derivatives = Sigmoid::derivative(&points);
147
148 let max_derivative = derivatives.iter().fold(0.0, |max, &val| max.max(val));
150 assert_abs_diff_eq!(max_derivative, 0.25, epsilon = 1e-8);
151 assert_abs_diff_eq!(derivatives[3], 0.25, epsilon = 1e-8); }
153}