rsneat 0.1.0

A simple Neuroevolution of Augmenting Topolgies implementation
Documentation
//! rsneat is an open source crate that implements the NEAT genetic algorithm. The implementation
//! is based off of the original [2002 paper][paper].
//!
//! [paper]: http://nn.cs.utexas.edu/downloads/papers/stanley.ec02.pdf
//!
//! The purpose of this project was to help me learn Rust and the Rust ecosystem. This project
//! should probably not be used for a serious project without extreme caution.
//!
//! # Example
//!
//! This example uses the neat simulation to find an algorithm that computes the xor function
//! ```rust,no_run
//!
//! let mut neat = Neat::new();
//! let mut founder = Genome::new(3, 1);
//! founder.add_connection(0, 3, 0.0, &mut neat);
//! founder.add_connection(1, 3, 0.0, &mut neat);
//! founder.add_connection(2, 3, 0.0, &mut neat);
//! 
//! let mut pop = Population::clone_from(founder, &neat);
//! loop {
//!     let mut correct = true;
//!     for (mut n, fitness) in pop.iter_fitness() {
//!         correct = true;
//!         let data = [
//!             (vec![0.0, 0.0, 1.0], 0.0, false),
//!             (vec![0.0, 1.0, 1.0], 1.0, true),
//!             (vec![1.0, 0.0, 1.0], 1.0, true),
//!             (vec![1.0, 1.0, 1.0], 0.0, false),
//!         ];
//! 
//!         let mut error = 0.0;
//!         *fitness = 0.0;
//!         for (input, output, c) in data.iter() {
//!             for _ in 0..5 {
//!                 let _ = n.activate(input.clone().into_iter());
//!             }
//!             let result = n.activate(input.clone().into_iter()).next().unwrap();
//!             let result_error = (result - output).abs();
//!             error += result_error;
//! 
//!             let result = result >= 0.5;
//!             if *c != result {
//!                 correct = false;
//!             }
//! 
//!             n.reset();
//!         }
//!         *fitness += 4.0 - error;
//!         *fitness *= *fitness;
//!         if correct {
//!             break;
//!         }
//!     }
//!     if correct {
//!         pop.champ().write(&mut std::fs::File::create("champ.neat").unwrap()).unwrap();
//!         println!("Got a champ after {} gens!", pop.gen());
//!     }
//!     pop.evaluate_generation(&mut neat);
//! }
//! ```

pub mod genome;
pub mod network;
pub mod population;
mod util;

use std::collections::HashMap;

/// Structure for NEAT hyperparameters
pub struct NeatParams {
	pub population_size: usize,
	pub delta_c1: f64,
	pub delta_c2: f64,
	pub delta_c3: f64,
	pub delta_threshold: f64,
	pub species_death_time: usize,
	pub champion_copy_size: usize,
	pub weight_mutation_chance: f64,
	pub weight_reset_chance: f64,
	pub disable_chance: f64,
	pub no_crossover_chance: f64,
	pub interspecies_mating_rate: f64,
	pub new_neuron_chance: f64,
	pub new_weight_chance: f64,
	pub survival_threshold: f64,
}

impl Default for NeatParams {
    fn default() -> Self {
        Self {
			population_size: 150,
			// population_size: 1000,
			delta_c1: 1.0,
			delta_c2: 1.0,
			// delta_c3: 0.4,
			delta_c3: 1.0,
			delta_threshold: 3.0,
			species_death_time: 15,
			champion_copy_size: 5,
			weight_mutation_chance: 0.8,
			weight_reset_chance: 0.1,
			disable_chance: 0.75,
			no_crossover_chance: 0.25,
			// interspecies_mating_rate: 0.001,
			interspecies_mating_rate: 0.05,
			// new_neuron_chance: 0.03,
			new_neuron_chance: 0.08,
			// new_weight_chance: 0.05,
			new_weight_chance: 0.1,
			survival_threshold: 0.4,
        }
    }
}

/// Structure for NEAT simulation state
pub struct Neat {
    params: NeatParams,
	innovations: HashMap<(usize, usize), usize>,
	innovation: usize,
}

impl Neat {

    /// Create a NEAT state with the default hyperparmeters
    pub fn new() -> Self {
        Self::from_params(NeatParams::default())
    }

    /// Create a NEAT state with the given hyperparmeters
	pub fn from_params(params: NeatParams) -> Neat {
		Neat {
            params,
			innovations: HashMap::new(),
			innovation: 0,
		}
	}

	fn innovation(&mut self, input: usize, output: usize) -> usize {
		match self.innovations.get(&(input, output)) {
			Some(result) => *result,
			None => {
				let result = self.innovation;
				self.innovations.insert((input, output), result);
				self.innovation += 1;
				result
			}
		}
	}

	pub fn population_size(&self) -> usize {
		self.params.population_size
	}
	pub fn delta_c1(&self) -> f64 {
		self.params.delta_c1
	}
	pub fn delta_c2(&self) -> f64 {
		self.params.delta_c2
	}
	pub fn delta_c3(&self) -> f64 {
		self.params.delta_c3
	}
	pub fn delta_threshold(&self) -> f64 {
		self.params.delta_threshold
	}
	pub fn species_death_time(&self) -> usize {
		self.params.species_death_time
	}
	pub fn champion_copy_size(&self) -> usize {
		self.params.champion_copy_size
	}
	pub fn weight_mutation_chance(&self) -> f64 {
		self.params.weight_mutation_chance
	}
	pub fn weight_reset_chance(&self) -> f64 {
		self.params.weight_reset_chance
	}
	pub fn disable_chance(&self) -> f64 {
		self.params.disable_chance
	}
	pub fn no_crossover_chance(&self) -> f64 {
		self.params.no_crossover_chance
	}
	pub fn interspecies_mating_rate(&self) -> f64 {
		self.params.interspecies_mating_rate
	}
	pub fn new_neuron_chance(&self) -> f64 {
		self.params.new_neuron_chance
	}
	pub fn new_weight_chance(&self) -> f64 {
		self.params.new_weight_chance
	}
	pub fn survival_threshold(&self) -> f64 {
		self.params.survival_threshold
	}
}

#[cfg(test)]
mod tests {}