frlearn_math/
transformations.rs1use ndarray::{Array1, Array2};
2
3pub fn clamp01(value: f64) -> f64 {
4 if !value.is_finite() {
5 return 0.0;
6 }
7
8 value.clamp(0.0, 1.0)
9}
10
11pub fn complement(value: f64) -> f64 {
12 clamp01(1.0 - clamp01(value))
13}
14
15pub fn safe_divide(numerator: f64, denominator: f64, default: f64) -> f64 {
16 if !numerator.is_finite() || !denominator.is_finite() || denominator.abs() <= f64::EPSILON {
17 return default;
18 }
19
20 let ratio = numerator / denominator;
21 if ratio.is_finite() { ratio } else { default }
22}
23
24pub fn row_sums(matrix: &Array2<f64>) -> Array1<f64> {
25 matrix
26 .rows()
27 .into_iter()
28 .map(|row| {
29 row.iter()
30 .copied()
31 .filter(|value| value.is_finite())
32 .sum::<f64>()
33 })
34 .collect()
35}
36
37pub fn safe_normalize_rows(matrix: &Array2<f64>) -> Array2<f64> {
38 let n_rows = matrix.nrows();
39 let n_cols = matrix.ncols();
40 let mut normalized = Array2::<f64>::zeros((n_rows, n_cols));
41
42 if n_cols == 0 {
43 return normalized;
44 }
45
46 for row_idx in 0..n_rows {
47 let mut sum = 0.0;
48 for col_idx in 0..n_cols {
49 let value = matrix[[row_idx, col_idx]];
50 let sanitized = if value.is_finite() && value > 0.0 {
51 value
52 } else {
53 0.0
54 };
55 normalized[[row_idx, col_idx]] = sanitized;
56 sum += sanitized;
57 }
58
59 if sum > 0.0 && sum.is_finite() {
60 for col_idx in 0..n_cols {
61 normalized[[row_idx, col_idx]] /= sum;
62 }
63 } else {
64 let uniform = 1.0 / n_cols as f64;
65 for col_idx in 0..n_cols {
66 normalized[[row_idx, col_idx]] = uniform;
67 }
68 }
69 }
70
71 normalized
72}