rlevo_evolution/
shaping.rs1use burn::prelude::ElementConversion;
10use burn::tensor::{backend::Backend, Tensor};
11
12#[must_use]
18pub fn z_score<B: Backend>(fitness: Tensor<B, 1>) -> Tensor<B, 1> {
19 let mean = fitness.clone().mean().into_scalar().elem::<f32>();
20 let n = fitness.shape().dims[0];
21 #[allow(clippy::cast_precision_loss)]
22 let n_f = n.max(1) as f32;
23 let centered = fitness - mean;
24 let var = centered.clone().powf_scalar(2.0).sum().into_scalar().elem::<f32>() / n_f;
25 let std = var.sqrt().max(1e-8);
26 centered / std
27}
28
29#[must_use]
45pub fn centered_rank<B: Backend>(fitness: Tensor<B, 1>, device: &B::Device) -> Tensor<B, 1> {
46 let data = fitness
47 .into_data()
48 .into_vec::<f32>()
49 .expect("centered_rank requires f32 tensor data");
50 let n = data.len();
51 if n == 0 {
52 return Tensor::<B, 1>::from_floats([0.0f32; 0], device);
53 }
54 let mut indices: Vec<usize> = (0..n).collect();
55 indices.sort_by(|&i, &j| data[i].partial_cmp(&data[j]).unwrap_or(std::cmp::Ordering::Equal));
56
57 #[allow(clippy::cast_precision_loss)]
58 let n_f = (n - 1).max(1) as f32;
59 let mut ranks = vec![0.0_f32; n];
60 for (rank, &idx) in indices.iter().enumerate() {
61 #[allow(clippy::cast_precision_loss)]
62 let r = rank as f32 / n_f - 0.5;
63 ranks[idx] = r;
64 }
65 Tensor::<B, 1>::from_floats(ranks.as_slice(), device)
66}
67
68#[cfg(test)]
69mod tests {
70 use super::*;
71 use burn::backend::NdArray;
72 type TestBackend = NdArray;
73
74 #[test]
75 #[allow(clippy::cast_precision_loss)]
76 fn z_score_zero_mean_unit_std() {
77 let device = Default::default();
78 let t = Tensor::<TestBackend, 1>::from_floats([1.0f32, 2.0, 3.0, 4.0, 5.0], &device);
79 let z = z_score(t);
80 let values = z.into_data().into_vec::<f32>().unwrap();
81 let mean: f32 = values.iter().sum::<f32>() / values.len() as f32;
82 approx::assert_relative_eq!(mean, 0.0, epsilon = 1e-5);
83 }
84
85 #[test]
86 fn centered_rank_spans_half_interval() {
87 let device = Default::default();
88 let t = Tensor::<TestBackend, 1>::from_floats([10.0f32, 20.0, 30.0, 40.0], &device);
89 let r = centered_rank(t, &device);
90 let values = r.into_data().into_vec::<f32>().unwrap();
91 approx::assert_relative_eq!(values[0], -0.5, epsilon = 1e-6);
93 approx::assert_relative_eq!(values[3], 0.5, epsilon = 1e-6);
94 }
95
96 #[test]
97 fn centered_rank_preserves_order() {
98 let device = Default::default();
99 let t = Tensor::<TestBackend, 1>::from_floats([3.0f32, 1.0, 2.0], &device);
100 let r = centered_rank(t, &device);
101 let values = r.into_data().into_vec::<f32>().unwrap();
102 approx::assert_relative_eq!(values[1], -0.5, epsilon = 1e-6);
105 approx::assert_relative_eq!(values[2], 0.0, epsilon = 1e-6);
106 approx::assert_relative_eq!(values[0], 0.5, epsilon = 1e-6);
107 }
108}