Skip to main content

frlearn_math/
transformations.rs

1use 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}