Skip to main content

data_beans/alg/
normalization.rs

1/// normalize distance
2pub trait NormalizeDistance: Iterator<Item = f32> + Sized {
3    fn exp_sum(self, lambda: f32) -> f32
4    where
5        Self: Sized,
6    {
7        self.map(|d| (-d * lambda).exp()).sum()
8    }
9
10    fn normalized_exp(self, target_exp_sum: f32) -> Vec<f32>
11    where
12        Self: Clone,
13    {
14        // a. discount distance values by the minimum
15        let dmin = self.clone().fold(f32::INFINITY, |a, b| a.min(b));
16        let dist = self.map(move |d| d - dmin);
17
18        // b. find optimum lambda value by a line search
19        let mut lambda = 10.0;
20        let mut fval = target_exp_sum - dist.clone().exp_sum(lambda);
21        let max_iter = 100;
22
23        for _ in 0..max_iter {
24            let lambda_old = lambda;
25            if fval < 0.0 {
26                lambda *= 2.0; // total weight sum > target_exp_sum
27            } else {
28                lambda *= 0.5; // total weight sum < target_exp_sum
29            }
30
31            let fval_new = target_exp_sum - dist.clone().exp_sum(lambda);
32
33            if fval_new.abs() > fval.abs() {
34                lambda = lambda_old;
35                break;
36            }
37
38            fval = fval_new;
39        }
40
41        // c. final weight calibration
42        dist.map(|v| (-v * lambda).exp()).collect()
43    }
44}
45
46impl<I> NormalizeDistance for I where I: Iterator<Item = f32> {}