neat_lib 0.1.1

Rust implementation of NeuroEvolution of Augmenting Topologies
Documentation
use serde::{Deserialize, Serialize};
use crate::neat::math::clamp;
use rand_distr::{Normal, Distribution};
use std::ops::{Add, Sub, Mul, Div, AddAssign};
use rand::Rng;

#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct FloatAttribute(f64);

impl FloatAttribute {
    pub fn new(value: f64) -> Self {
        FloatAttribute(value)
    }

    pub fn new_norm(mean: f64, std_dev: f64) -> Self {
        let normal = Normal::new(mean, std_dev).unwrap();
        FloatAttribute(normal.sample(&mut rand::thread_rng()))
    }

    pub fn abs(&self) -> f64 {
        self.0.abs()
    }

    pub fn mutate(&mut self, replace_rate: f64, mutate_rate: f64, mutate_power: f64, min: f64, max: f64, mean: f64, std_dev: f64 ) {
        let mut rng = rand::thread_rng();
        let rand = rng.gen_range(0.0..1.0);
        if rand < mutate_rate {
            let normal = Normal::new(0.0, mutate_power).unwrap();
            self.0 = clamp(self.0 + normal.sample(&mut rng), min, max);
            return
        }

        if rand < replace_rate + mutate_rate {
            self.0 = FloatAttribute::init_value(&mut rng, mean, std_dev, min, max)
        }
    }

    pub fn set_value(&mut self, value: f64) {
        self. 0 = value
    }

    pub fn value(&self) -> f64 {
        self.0
    }

    fn init_value(rng: &mut impl Rng, mean: f64, std_dev: f64, min: f64, max: f64) -> f64 {
        let normal = Normal::new(mean, std_dev).unwrap();
        clamp(normal.sample(rng), min, max)
    }
}

impl Add for FloatAttribute {
    type Output = Self;
    fn add(self, rhs: Self) -> Self::Output {
        return FloatAttribute(self.value() + rhs.value())
    }
}

impl Sub for FloatAttribute {
    type Output = Self;
    fn sub(self, rhs: Self) -> Self::Output {
        return FloatAttribute(self.value() - rhs.value())
    }
}

impl Mul for FloatAttribute {
    type Output = Self;
    fn mul(self, rhs: Self) -> Self::Output {
        return FloatAttribute(self.value() * rhs.value())
    }
}


impl Div for FloatAttribute {
    type Output = Self;
    fn div(self, rhs: Self) -> Self::Output {
        return FloatAttribute(self.value() / rhs.value())
    }
}

impl AddAssign for FloatAttribute {
    fn add_assign(&mut self, rhs: Self) {
        self.set_value(self.value() + rhs.value())
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_float_attribute_mutate() {
        let mut float = FloatAttribute::new(0.0);
        float.mutate(0.0, 1.0, 1.0, -1.0, 1.0, 0.0, 0.0);
        assert!(float.value() <= 1.0, "float should be smaller than 1.0")
    }

    #[test]
    fn test_float_attribute_ops() {
        let float1 = FloatAttribute::new(1.0);
        let float2 = FloatAttribute::new(1.0);
        assert!((float1 + float2).value() == 2.0, "+");
        assert!((float1 - float2).value() == 0.0, "-");
        assert!((float1 * float2).value() == 1.0, "*");
        assert!((float1 / float2).value() == 1.0, "/");
    }
}