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}