metrics.rs
2.4 kB · rust · 82 lines
1use crate::two::Cell2d;2use mrlycore::logs;3use mrlycore::tensor::Tensor;4use std::f64::consts::LN_2;56/// Returns the mean fraction of sites changed between consecutive grids.7pub fn churn(grids: &[Cell2d]) -> f64 {8 if grids.len() < 2 {9 return 0.0;10 }11 let total: f64 = grids12 .windows(2)13 .map(|pair| spread(pair[0].types(), pair[1].types()))14 .sum();15 total / (grids.len() - 1) as f6416}1718/// Returns the grid's binary Shannon entropy in millibits.19pub fn entropy(grid: &Cell2d) -> i64 {20 let bytes = grid.types().bytes();21 let total = bytes.len();22 if total == 0 {23 return 0;24 }25 let ones = bytes.iter().filter(|&&b| b == 1).count();26 let p = ones as f64 / total as f64;27 if p == 0.0 || p == 1.0 {28 return 0;29 }30 let bits = -(p * (logs::ln(p) / LN_2) + (1.0 - p) * (logs::ln(1.0 - p) / LN_2));31 (bits * 1000.0).round() as i6432}3334fn spread(a: &Tensor, b: &Tensor) -> f64 {35 if a.shape != b.shape || a.size() == 0 {36 return 1.0;37 }38 let differing = (0..a.size()).filter(|&i| a.at(i) != b.at(i)).count();39 differing as f64 / a.size() as f6440}4142#[cfg(test)]43mod tests {44 use super::*;45 use mrlycore::tensor::Tensor;4647 fn grid(bits: &[u8], side: usize) -> Cell2d {48 Cell2d::new(Tensor::of(bits.to_vec(), vec![side, side]))49 }5051 #[test]52 fn empty_and_full_have_no_entropy() {53 assert_eq!(entropy(&grid(&[0, 0, 0, 0], 2)), 0);54 assert_eq!(entropy(&grid(&[1, 1, 1, 1], 2)), 0);55 }56 #[test]57 fn half_filled_is_one_bit() {58 assert_eq!(entropy(&grid(&[1, 0, 0, 1], 2)), 1000);59 }60 #[test]61 fn quarter_filled_matches_shannon() {62 assert_eq!(entropy(&grid(&[1, 0, 0, 0], 2)), 811);63 }64 #[test]65 fn churn_averages_the_changed_fractions() {66 let a = grid(&[0, 0, 0, 0], 2);67 let b = grid(&[1, 0, 0, 0], 2);68 let c = grid(&[0, 1, 0, 0], 2);69 assert_eq!(churn(&[]), 0.0);70 assert_eq!(churn(std::slice::from_ref(&a)), 0.0);71 assert_eq!(churn(&[a.clone(), b.clone()]), 0.25);72 assert_eq!(churn(&[a.clone(), b, c]), 0.375);73 assert_eq!(churn(&[a.clone(), a]), 0.0);74 }75 #[test]76 fn churn_reads_the_spread_of_each_pair() {77 let flat = grid(&[0, 0, 0, 0], 2);78 let dots = grid(&[1, 0, 0, 1], 2);79 assert_eq!(spread(flat.types(), dots.types()), 0.5);80 assert_eq!(churn(&[flat, dots]), 0.5);81 }82}