├── .gitignore ├── src ├── utility │ ├── mod.rs │ ├── rng.rs │ ├── statistics.rs │ ├── gym.rs │ └── favannat_impl.rs ├── runtime │ ├── evaluation.rs │ ├── progress.rs │ └── mod.rs ├── genes │ ├── id │ │ ├── mod.rs │ │ ├── id_iter.rs │ │ └── id_generator.rs │ ├── weights.rs │ ├── activations.rs │ ├── nodes.rs │ ├── connections.rs │ └── mod.rs ├── lib.rs ├── parameters.rs ├── individual │ ├── mod.rs │ ├── behavior.rs │ ├── scores.rs │ └── genome.rs └── population.rs └── Cargo.toml /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | 4 | /**/*.log 5 | -------------------------------------------------------------------------------- /src/utility/mod.rs: -------------------------------------------------------------------------------- 1 | mod favannat_impl; 2 | pub mod gym; 3 | pub mod rng; 4 | pub mod statistics; 5 | -------------------------------------------------------------------------------- /src/runtime/evaluation.rs: -------------------------------------------------------------------------------- 1 | use crate::{individual::Individual, utility::statistics::Statistics}; 2 | 3 | pub enum Evaluation { 4 | Progress(Statistics), 5 | Solution(Individual), 6 | } 7 | -------------------------------------------------------------------------------- /src/genes/id/mod.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | pub mod id_generator; 4 | pub mod id_iter; 5 | 6 | #[derive(PartialEq, Eq, Debug, Clone, Copy, Hash, Serialize, Deserialize, PartialOrd, Ord)] 7 | pub struct Id(pub usize); 8 | -------------------------------------------------------------------------------- /src/genes/weights.rs: -------------------------------------------------------------------------------- 1 | use std::ops::{Deref, DerefMut}; 2 | 3 | use serde::{Deserialize, Serialize}; 4 | 5 | #[derive(Clone, Copy, Debug, Serialize, Deserialize)] 6 | pub struct Weight(pub f64); 7 | 8 | impl Deref for Weight { 9 | type Target = f64; 10 | 11 | fn deref(&self) -> &Self::Target { 12 | &self.0 13 | } 14 | } 15 | 16 | impl DerefMut for Weight { 17 | fn deref_mut(&mut self) -> &mut Self::Target { 18 | &mut self.0 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "novel-set-neat" 3 | version = "0.1.0" 4 | authors = ["Silvan Buedenbender "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | serde = { version = "1.0", features = ["derive"] } 11 | rand = { version = "0.7.3", features = [ "small_rng" ] } 12 | rand_distr = "0.2" 13 | config = "0.9" 14 | ndarray = { version = "0.13.0", features = ["serde"] } 15 | gym = { path = "../gym-rs" } 16 | rayon = "1.3" 17 | favannat = { path = "../favannat" } 18 | 19 | [dev-dependencies] 20 | gym = { path = "../gym-rs" } 21 | criterion = "0.3" 22 | log = "0.4.11" 23 | log4rs = "0.13.0" 24 | serde_json = "1.0" 25 | -------------------------------------------------------------------------------- /src/utility/rng.rs: -------------------------------------------------------------------------------- 1 | use rand::{prelude::SmallRng, Rng, SeedableRng}; 2 | use rand_distr::{Distribution, Normal}; 3 | 4 | #[derive(Debug)] 5 | pub struct NeatRng { 6 | pub small: SmallRng, 7 | pub weight_distribution: Normal, 8 | } 9 | 10 | impl NeatRng { 11 | pub fn new(seed: u64, std_dev: f64) -> Self { 12 | Self { 13 | small: SmallRng::seed_from_u64(seed), 14 | weight_distribution: Normal::new(0.0, std_dev) 15 | .expect("could not create weight distribution"), 16 | } 17 | } 18 | 19 | pub fn gamble(&mut self, chance: f64) -> bool { 20 | self.small.gen::() < chance 21 | } 22 | 23 | pub fn weight_perturbation(&mut self) -> f64 { 24 | self.weight_distribution.sample(&mut self.small) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub use individual::Individual; 2 | use parameters::Parameters; 3 | pub use runtime::{evaluation::Evaluation, progress::Progress, Runtime}; 4 | 5 | mod genes; 6 | mod individual; 7 | mod parameters; 8 | mod population; 9 | mod runtime; 10 | pub mod utility; 11 | 12 | pub struct Neat { 13 | pub parameters: Parameters, 14 | progress_function: Box Progress + Send + Sync>, 15 | } 16 | 17 | // public API 18 | impl Neat { 19 | pub fn new( 20 | path: &str, 21 | progress_function: Box Progress + Send + Sync>, 22 | ) -> Self { 23 | Neat { 24 | parameters: Parameters::new(path).unwrap(), 25 | progress_function, 26 | } 27 | } 28 | 29 | pub fn run(&self) -> Runtime { 30 | Runtime::new(&self) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/genes/activations.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] 4 | pub enum Activation { 5 | Linear, 6 | Sigmoid, 7 | Tanh, 8 | Gaussian, 9 | Step, 10 | Sine, 11 | Cosine, 12 | Inverse, 13 | Absolute, 14 | Relu, 15 | Squared, 16 | } 17 | 18 | impl Default for Activation { 19 | fn default() -> Self { 20 | Activation::Tanh 21 | } 22 | } 23 | 24 | pub const LINEAR: fn(f64) -> f64 = |val| val; 25 | // pub const SIGMOID: fn(f64) -> f64 = |val| 1.0 / (1.0 + (-1.0 * val).exp()); 26 | pub const SIGMOID: fn(f64) -> f64 = |val| 1.0 / (1.0 + (-4.9 * val).exp()); 27 | pub const TANH: fn(f64) -> f64 = |val| 2.0 * SIGMOID(2.0 * val) - 1.0; 28 | pub const GAUSSIAN: fn(f64) -> f64 = |val| (val * val / -2.0).exp(); // a = 1, b = 0, c = 1 29 | pub const STEP: fn(f64) -> f64 = |val| if val > 0.0 { 1.0 } else { 0.0 }; 30 | pub const SINE: fn(f64) -> f64 = |val| (val * std::f64::consts::PI).sin(); 31 | pub const COSINE: fn(f64) -> f64 = |val| (val * std::f64::consts::PI).cos(); 32 | pub const INVERSE: fn(f64) -> f64 = |val| -val; 33 | pub const ABSOLUTE: fn(f64) -> f64 = |val| val.abs(); 34 | pub const RELU: fn(f64) -> f64 = |val| 0f64.max(val); 35 | pub const SQUARED: fn(f64) -> f64 = |val| val * val; 36 | -------------------------------------------------------------------------------- /src/genes/id/id_iter.rs: -------------------------------------------------------------------------------- 1 | use std::ops::RangeFrom; 2 | 3 | use super::Id; 4 | 5 | pub struct IdIter<'a> { 6 | index: usize, 7 | ids: &'a mut Vec, 8 | gen: &'a mut RangeFrom, 9 | } 10 | 11 | impl<'a> Iterator for IdIter<'a> { 12 | type Item = Id; 13 | 14 | fn next(&mut self) -> Option { 15 | if self.index < self.ids.len() { 16 | let index = self.index; 17 | self.index += 1; 18 | Some(self.ids[index]) 19 | } else { 20 | let id = self.gen.next().map(Id); 21 | self.ids.push(id.unwrap()); 22 | self.index += 1; 23 | id 24 | } 25 | } 26 | } 27 | 28 | impl<'a> IdIter<'a> { 29 | pub fn new(ids: &'a mut Vec, gen: &'a mut RangeFrom) -> Self { 30 | IdIter { index: 0, ids, gen } 31 | } 32 | } 33 | 34 | #[cfg(test)] 35 | mod tests { 36 | 37 | use super::{Id, IdIter}; 38 | 39 | #[test] 40 | fn iterate_till_new() { 41 | let mut ids = vec![Id(4), Id(2)]; 42 | let mut gen = 0..; 43 | 44 | let mut test_id_iter = IdIter::new(&mut ids, &mut gen); 45 | 46 | assert_eq!(test_id_iter.next(), Some(Id(4))); 47 | assert_eq!(test_id_iter.next(), Some(Id(2))); 48 | assert_eq!(test_id_iter.next(), Some(Id(0))); // generate and cache new id 49 | assert_eq!(test_id_iter.next(), Some(Id(1))); // generate and cache new id 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/utility/statistics.rs: -------------------------------------------------------------------------------- 1 | use serde::Serialize; 2 | 3 | use crate::individual::Individual; 4 | 5 | #[derive(Debug, Clone, Default, Serialize)] 6 | pub struct Statistics { 7 | pub population: PopulationStatistics, 8 | pub num_generation: usize, 9 | pub milliseconds_elapsed_evaluation: u128, 10 | pub time_stamp: u64, 11 | } 12 | 13 | #[derive(Debug, Clone, Default, Serialize)] 14 | pub struct FitnessStatisitcs { 15 | pub raw_maximum: f64, 16 | pub raw_minimum: f64, 17 | pub raw_average: f64, 18 | pub raw_std_dev: f64, 19 | pub shifted_maximum: f64, 20 | pub shifted_minimum: f64, 21 | pub shifted_average: f64, 22 | pub normalized_maximum: f64, 23 | pub normalized_minimum: f64, 24 | pub normalized_average: f64, 25 | } 26 | 27 | #[derive(Debug, Clone, Default, Serialize)] 28 | pub struct NoveltyStatisitcs { 29 | pub raw_maximum: f64, 30 | pub raw_minimum: f64, 31 | pub raw_average: f64, 32 | pub shifted_maximum: f64, 33 | pub shifted_minimum: f64, 34 | pub shifted_average: f64, 35 | pub normalized_maximum: f64, 36 | pub normalized_minimum: f64, 37 | pub normalized_average: f64, 38 | } 39 | #[derive(Debug, Clone, Default, Serialize)] 40 | pub struct PopulationStatistics { 41 | pub milliseconds_elapsed_reproducing: u128, 42 | pub top_performer: Individual, 43 | pub age_maximum: usize, 44 | pub age_average: f64, 45 | pub fitness: FitnessStatisitcs, 46 | pub novelty: NoveltyStatisitcs, 47 | } 48 | -------------------------------------------------------------------------------- /src/genes/id/id_generator.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::HashMap, ops::RangeFrom}; 2 | 3 | use super::{id_iter::IdIter, Id}; 4 | 5 | #[derive(Debug)] 6 | pub struct IdGenerator { 7 | id_gen: RangeFrom, 8 | id_cache: HashMap<(Id, Id), Vec>, 9 | } 10 | 11 | impl Default for IdGenerator { 12 | fn default() -> Self { 13 | IdGenerator { 14 | id_gen: 0.., 15 | id_cache: HashMap::new(), 16 | } 17 | } 18 | } 19 | 20 | impl IdGenerator { 21 | pub fn next_id(&mut self) -> Id { 22 | self.id_gen.next().map(Id).unwrap() 23 | } 24 | pub fn cached_id_iter(&mut self, cache_key: (Id, Id)) -> IdIter { 25 | let cache_entry = self.id_cache.entry(cache_key).or_insert_with(Vec::new); 26 | IdIter::new(cache_entry, &mut self.id_gen) 27 | } 28 | } 29 | 30 | #[cfg(test)] 31 | mod tests { 32 | 33 | use super::{Id, IdGenerator}; 34 | 35 | #[test] 36 | fn get_new_id() { 37 | let mut test_id_manager = IdGenerator::default(); 38 | 39 | assert_eq!(test_id_manager.next_id(), Id(0)); 40 | assert_eq!(test_id_manager.next_id(), Id(1)); 41 | assert_eq!(test_id_manager.next_id(), Id(2)); 42 | } 43 | 44 | #[test] 45 | fn iter_cached_ids() { 46 | let mut test_id_manager = IdGenerator::default(); 47 | 48 | let mut test_id_iter_0 = test_id_manager.cached_id_iter((Id(4), Id(2))); 49 | 50 | assert_eq!(test_id_iter_0.next(), Some(Id(0))); 51 | assert_eq!(test_id_iter_0.next(), Some(Id(1))); 52 | 53 | let mut test_id_iter_1 = test_id_manager.cached_id_iter((Id(4), Id(2))); 54 | 55 | assert_eq!(test_id_iter_1.next(), Some(Id(0))); // cached entry 56 | assert_eq!(test_id_iter_1.next(), Some(Id(1))); // cached entry 57 | assert_eq!(test_id_iter_1.next(), Some(Id(2))); // new entry 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/genes/nodes.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use std::{ 3 | cmp::Ordering, 4 | hash::{Hash, Hasher}, 5 | ops::{Deref, DerefMut}, 6 | }; 7 | 8 | use super::{Activation, Gene, Id}; 9 | 10 | pub trait NodeSpecifier {} 11 | 12 | pub trait NodeMarker {} 13 | 14 | #[derive(Debug, Clone, Deserialize, Serialize)] 15 | pub struct Node(pub Id, pub Activation); 16 | 17 | impl NodeMarker for Node {} 18 | 19 | impl Node { 20 | pub fn id(&self) -> Id { 21 | self.0 22 | } 23 | } 24 | 25 | impl Gene for Node {} 26 | 27 | impl Hash for Node { 28 | fn hash(&self, state: &mut H) { 29 | self.0.hash(state) 30 | } 31 | } 32 | 33 | impl PartialEq for Node { 34 | fn eq(&self, other: &Self) -> bool { 35 | self.0 == other.0 36 | } 37 | } 38 | 39 | impl PartialEq for Node { 40 | fn eq(&self, other: &Id) -> bool { 41 | &self.0 == other 42 | } 43 | } 44 | 45 | impl Eq for Node {} 46 | 47 | impl PartialOrd for Node { 48 | fn partial_cmp(&self, other: &Self) -> Option { 49 | Some(self.cmp(&other)) 50 | } 51 | } 52 | 53 | impl Ord for Node { 54 | fn cmp(&self, other: &Self) -> Ordering { 55 | self.0.cmp(&other.0) 56 | } 57 | } 58 | 59 | macro_rules! makeNodeSpecifier { 60 | ( $( $name:ident ),* ) => { 61 | $( 62 | #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, PartialOrd, Ord, Deserialize, Serialize)] 63 | pub struct $name(pub T); 64 | 65 | impl NodeSpecifier for $name {} 66 | 67 | impl Deref for $name { 68 | type Target = T; 69 | 70 | fn deref(&self) -> &Self::Target { 71 | &self.0 72 | } 73 | } 74 | 75 | impl DerefMut for $name { 76 | fn deref_mut(&mut self) -> &mut Self::Target { 77 | &mut self.0 78 | } 79 | } 80 | )* 81 | }; 82 | } 83 | 84 | makeNodeSpecifier!(Input, Hidden, Output); 85 | -------------------------------------------------------------------------------- /src/utility/gym.rs: -------------------------------------------------------------------------------- 1 | use gym::State; 2 | use ndarray::{stack, Array1, Array2, ArrayView1, ArrayView2, ArrayViewMut1, Axis}; 3 | use serde::{Deserialize, Serialize}; 4 | 5 | #[derive(Debug, Clone, Deserialize, Serialize)] 6 | pub struct StandardScaler { 7 | means: Array1, 8 | standard_deviations: Array1, 9 | } 10 | 11 | impl StandardScaler { 12 | pub fn for_environment(environment: &str) -> Self { 13 | Self::new(Self::generate_standard_scaler_samples(environment, 1000).view()) 14 | } 15 | 16 | pub fn new(samples: ArrayView2) -> Self { 17 | let standard_deviations = samples 18 | .var_axis(Axis(0), 0.0) 19 | .mapv_into(|x| (x + f64::EPSILON).sqrt()); 20 | 21 | let means = samples.mean_axis(Axis(0)).unwrap(); 22 | 23 | StandardScaler { 24 | means, 25 | standard_deviations, 26 | } 27 | } 28 | 29 | pub fn scale_inplace(&self, mut sample: ArrayViewMut1) { 30 | sample -= &self.means; 31 | sample /= &self.standard_deviations; 32 | } 33 | 34 | pub fn scale(&self, sample: ArrayView1) -> Array1 { 35 | (&sample - &self.means) / &self.standard_deviations 36 | } 37 | 38 | fn generate_standard_scaler_samples(environment: &str, num_samples: usize) -> Array2 { 39 | let gym = gym::GymClient::default(); 40 | let env = gym.make(environment); 41 | 42 | // collect samples for standard scaler 43 | let samples = env 44 | .reset() 45 | .unwrap() 46 | .get_box() 47 | .expect("expected gym environment with box type observations"); 48 | 49 | let mut samples = samples.insert_axis(Axis(0)); 50 | 51 | println!("sampling for scaler"); 52 | for _ in 0..num_samples { 53 | let State { observation, .. } = env.step(&env.action_space().sample()).unwrap(); 54 | 55 | samples = stack![ 56 | Axis(0), 57 | samples, 58 | observation.get_box().unwrap().insert_axis(Axis(0)) 59 | ]; 60 | } 61 | println!("done sampling"); 62 | 63 | samples 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/runtime/progress.rs: -------------------------------------------------------------------------------- 1 | use crate::individual::{ 2 | behavior::Behavior, 3 | scores::{Fitness, Raw}, 4 | Individual, 5 | }; 6 | 7 | #[derive(Debug)] 8 | pub enum Progress { 9 | Empty, 10 | Novelty(Behavior), 11 | Status(Raw, Behavior), 12 | Solution(Option>, Option, Box), 13 | } 14 | 15 | impl Progress { 16 | pub fn new(fitness: f64, behavior: Vec) -> Self { 17 | Progress::Status(Raw::fitness(fitness), Behavior(behavior)) 18 | } 19 | 20 | pub fn empty() -> Self { 21 | Progress::Empty 22 | } 23 | 24 | pub fn solved(self, solution: Individual) -> Self { 25 | match self { 26 | Progress::Novelty(behavior) => { 27 | Progress::Solution(None, Some(behavior), Box::new(solution)) 28 | } 29 | Progress::Status(fitness, behavior) => { 30 | Progress::Solution(Some(fitness), Some(behavior), Box::new(solution)) 31 | } 32 | Progress::Solution(fitness, behavior, _) => { 33 | Progress::Solution(fitness, behavior, Box::new(solution)) 34 | } 35 | Progress::Empty => Progress::Solution(None, None, Box::new(solution)), 36 | } 37 | } 38 | 39 | pub fn novelty(behavior: Vec) -> Self { 40 | Self::Novelty(Behavior(behavior)) 41 | } 42 | 43 | pub fn behavior(&self) -> Option<&Behavior> { 44 | match self { 45 | Progress::Status(_, behavior) => Some(behavior), 46 | Progress::Solution(_, behavior, _) => behavior.as_ref(), 47 | Progress::Novelty(behavior) => Some(behavior), 48 | Progress::Empty => None, 49 | } 50 | } 51 | 52 | pub fn raw_fitness(&self) -> Option> { 53 | match *self { 54 | Progress::Status(fitness, _) => Some(fitness), 55 | Progress::Solution(fitness, _, _) => fitness, 56 | Progress::Novelty(_) => None, 57 | Progress::Empty => None, 58 | } 59 | } 60 | 61 | pub fn is_solution(&self) -> Option<&Individual> { 62 | match self { 63 | Progress::Solution(_, _, individual) => Some(individual), 64 | _ => None, 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/runtime/mod.rs: -------------------------------------------------------------------------------- 1 | use std::time::{Instant, SystemTime}; 2 | 3 | use crate::{ 4 | individual::Individual, population::Population, utility::statistics::Statistics, Neat, 5 | }; 6 | 7 | use rayon::prelude::{IntoParallelRefIterator, ParallelIterator}; 8 | 9 | use self::{evaluation::Evaluation, progress::Progress}; 10 | 11 | pub mod evaluation; 12 | pub mod progress; 13 | 14 | pub struct Runtime<'a> { 15 | neat: &'a Neat, 16 | population: Population, 17 | statistics: Statistics, 18 | } 19 | 20 | impl<'a> Runtime<'a> { 21 | pub fn new(neat: &'a Neat) -> Self { 22 | Self { 23 | neat, 24 | population: Population::new(&neat.parameters), 25 | statistics: Statistics::default(), 26 | } 27 | } 28 | 29 | fn generate_progress(&self) -> Vec { 30 | let progress_fn = &self.neat.progress_function; 31 | 32 | // apply progress function to every individual 33 | self.population 34 | .individuals() 35 | .par_iter() 36 | .map(progress_fn) 37 | .collect::>() 38 | } 39 | 40 | fn check_for_solution(&self, progress: &[Progress]) -> Option { 41 | progress 42 | .iter() 43 | .filter_map(|p| p.is_solution()) 44 | .cloned() 45 | .next() 46 | } 47 | } 48 | 49 | impl<'a> Iterator for Runtime<'a> { 50 | type Item = Evaluation; 51 | 52 | fn next(&mut self) -> Option { 53 | self.statistics.time_stamp = SystemTime::now() 54 | .duration_since(SystemTime::UNIX_EPOCH) 55 | .unwrap() 56 | .as_secs(); 57 | let now = Instant::now(); 58 | 59 | // generate progress by running progress function for every individual 60 | let progress = self.generate_progress(); 61 | 62 | self.statistics.num_generation += 1; 63 | self.statistics.milliseconds_elapsed_evaluation = now.elapsed().as_millis(); 64 | 65 | if let Some(winner) = self.check_for_solution(&progress) { 66 | Some(Evaluation::Solution(winner)) 67 | } else { 68 | self.statistics.population = self 69 | .population 70 | .next_generation(&self.neat.parameters, &progress); 71 | 72 | Some(Evaluation::Progress(self.statistics.clone())) 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/genes/connections.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use std::{ 3 | cmp::Ordering, 4 | hash::Hash, 5 | hash::Hasher, 6 | ops::{Deref, DerefMut}, 7 | }; 8 | 9 | use super::{Gene, Id, Weight}; 10 | 11 | pub trait ConnectionSpecifier {} 12 | 13 | pub trait ConnectionMarker {} 14 | 15 | #[derive(Debug, Clone, Serialize, Deserialize)] 16 | pub struct Connection(pub Id, pub Weight, pub Id); 17 | 18 | impl ConnectionMarker for Connection {} 19 | 20 | impl Connection { 21 | pub fn id(&self) -> (Id, Id) { 22 | (self.0, self.2) 23 | } 24 | pub fn input(&self) -> Id { 25 | self.0 26 | } 27 | pub fn output(&self) -> Id { 28 | self.2 29 | } 30 | pub fn adjust_weight(&mut self, adjustment: f64) { 31 | *self.1 += adjustment; 32 | } 33 | } 34 | impl Gene for Connection {} 35 | 36 | impl PartialEq for Connection { 37 | fn eq(&self, other: &Self) -> bool { 38 | self.0 == other.0 && self.2 == other.2 39 | } 40 | } 41 | 42 | impl Eq for Connection {} 43 | 44 | impl Hash for Connection { 45 | fn hash(&self, state: &mut H) { 46 | (self.0, self.2).hash(state); 47 | } 48 | } 49 | 50 | impl PartialOrd for Connection { 51 | fn partial_cmp(&self, other: &Self) -> Option { 52 | Some(self.cmp(other)) 53 | } 54 | } 55 | 56 | impl Ord for Connection { 57 | fn cmp(&self, other: &Self) -> Ordering { 58 | self.0.cmp(&other.0).then(self.2.cmp(&other.2)) 59 | } 60 | } 61 | 62 | macro_rules! makeConnectionSpecifier { 63 | ( $( $name:ident ),* ) => { 64 | $( 65 | #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, PartialOrd, Ord, Serialize, Deserialize)] 66 | pub struct $name(pub T); 67 | 68 | impl ConnectionSpecifier for $name {} 69 | 70 | impl Deref for $name { 71 | type Target = T; 72 | 73 | fn deref(&self) -> &Self::Target { 74 | &self.0 75 | } 76 | } 77 | 78 | impl DerefMut for $name { 79 | fn deref_mut(&mut self) -> &mut Self::Target { 80 | &mut self.0 81 | } 82 | } 83 | )* 84 | }; 85 | } 86 | 87 | makeConnectionSpecifier!(FeedForward, Recurrent); 88 | -------------------------------------------------------------------------------- /src/parameters.rs: -------------------------------------------------------------------------------- 1 | use crate::genes::Activation; 2 | use config::{Config, ConfigError, File}; 3 | use serde::{Deserialize, Serialize}; 4 | 5 | #[derive(Deserialize, Serialize, Default, Debug)] 6 | pub struct Parameters { 7 | pub setup: Setup, 8 | pub activations: Activations, 9 | pub mutation: Mutation, 10 | } 11 | 12 | #[derive(Deserialize, Serialize, Default, Debug)] 13 | pub struct Setup { 14 | pub seed: u64, 15 | pub survival_rate: f64, 16 | pub population_size: usize, 17 | pub input_dimension: usize, 18 | pub output_dimension: usize, 19 | pub novelty_nearest_neighbors: usize, 20 | } 21 | 22 | #[derive(Deserialize, Serialize, Debug)] 23 | pub struct Activations { 24 | pub output_nodes: Activation, 25 | pub hidden_nodes: Vec, 26 | } 27 | 28 | impl Default for Activations { 29 | fn default() -> Self { 30 | Self { 31 | output_nodes: Activation::Tanh, 32 | hidden_nodes: vec![ 33 | Activation::Linear, 34 | Activation::Sigmoid, 35 | Activation::Tanh, 36 | Activation::Gaussian, 37 | Activation::Step, 38 | Activation::Sine, 39 | Activation::Cosine, 40 | Activation::Inverse, 41 | Activation::Absolute, 42 | Activation::Relu, 43 | ], 44 | } 45 | } 46 | } 47 | 48 | #[derive(Deserialize, Serialize, Debug)] 49 | pub struct Mutation { 50 | pub new_node_chance: f64, 51 | pub new_connection_chance: f64, 52 | pub connection_is_recurrent_chance: f64, 53 | pub change_activation_function_chance: f64, 54 | pub weight_perturbation_std_dev: f64, 55 | } 56 | 57 | impl Default for Mutation { 58 | fn default() -> Self { 59 | Self { 60 | new_node_chance: 0.05, 61 | new_connection_chance: 0.1, 62 | connection_is_recurrent_chance: 0.3, 63 | change_activation_function_chance: 0.05, 64 | weight_perturbation_std_dev: 1.0, 65 | } 66 | } 67 | } 68 | 69 | impl Parameters { 70 | pub fn new(path: &str) -> Result { 71 | let mut s = Config::new(); 72 | 73 | // Start off by merging in the "default" configuration file 74 | s.merge(File::with_name(path))?; 75 | 76 | // You can deserialize (and thus freeze) the entire configuration as 77 | s.try_into() 78 | } 79 | } 80 | 81 | #[cfg(test)] 82 | mod tests { 83 | use super::Parameters; 84 | 85 | #[test] 86 | fn read_parameters() { 87 | todo!() 88 | /* let parameters = Parameters::new("src/Config.toml").unwrap(); 89 | 90 | assert_eq!(parameters.reproduction.stale_after, 15) */ 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/individual/mod.rs: -------------------------------------------------------------------------------- 1 | use std::ops::{Deref, DerefMut}; 2 | 3 | use rand::prelude::SmallRng; 4 | use serde::{Deserialize, Serialize}; 5 | 6 | use crate::{genes::IdGenerator, parameters::Parameters}; 7 | 8 | use self::scores::{FitnessScore, NoveltyScore, ScoreValue}; 9 | use self::{behavior::Behavior, genome::Genome}; 10 | 11 | pub mod behavior; 12 | pub mod genome; 13 | pub mod scores; 14 | 15 | #[derive(Debug, Clone, Default, Serialize, Deserialize)] 16 | pub struct Individual { 17 | pub genome: Genome, 18 | pub age: usize, 19 | pub behavior: Option, 20 | pub fitness: Option, 21 | pub novelty: Option, 22 | } 23 | 24 | impl Deref for Individual { 25 | type Target = Genome; 26 | 27 | fn deref(&self) -> &Self::Target { 28 | &self.genome 29 | } 30 | } 31 | 32 | impl DerefMut for Individual { 33 | fn deref_mut(&mut self) -> &mut Self::Target { 34 | &mut self.genome 35 | } 36 | } 37 | 38 | impl Individual { 39 | pub fn initial(id_gen: &mut IdGenerator, parameters: &Parameters) -> Self { 40 | Self { 41 | genome: Genome::new(id_gen, parameters), 42 | age: 0, 43 | behavior: None, 44 | fitness: None, 45 | novelty: None, 46 | } 47 | } 48 | 49 | // score is combination of fitness & novelty 50 | pub fn score(&self) -> f64 { 51 | let novelty = self 52 | .novelty 53 | .as_ref() 54 | .map(|n| n.normalized.value()) 55 | .unwrap_or(0.0); 56 | let fitness = self 57 | .fitness 58 | .as_ref() 59 | .map(|n| n.normalized.value()) 60 | .unwrap_or(0.0); 61 | 62 | novelty.max(fitness) 63 | 64 | // (novelty + fitness) / 2.0 65 | 66 | // novelty * 0.7 + fitness * 0.3 67 | 68 | /* if novelty == 0.0 && fitness == 0.0 { 69 | return 0.0; 70 | } 71 | 72 | let (min, max) = if novelty < fitness { 73 | (novelty, fitness) 74 | } else { 75 | (fitness, novelty) 76 | }; */ 77 | 78 | // ratio tells us what score is dominant in this genome 79 | // let ratio = min / max / 2.0; 80 | 81 | // we weight the scores by their ratio, i.e. a genome that has a good fitness value is primarily weighted by that 82 | // min * ratio + max * (1.0 - ratio) 83 | } 84 | 85 | // self is fitter if it has higher score or in case of equal score has fewer genes, i.e. less complexity 86 | pub fn is_fitter_than(&self, other: &Self) -> bool { 87 | let score_self = self.score(); 88 | let score_other = other.score(); 89 | 90 | score_self > score_other 91 | || ((score_self - score_other).abs() < f64::EPSILON 92 | && self.genome.len() < other.genome.len()) 93 | } 94 | 95 | pub fn crossover(&self, other: &Self, rng: &mut SmallRng) -> Self { 96 | let (fitter, weaker) = if self.is_fitter_than(other) { 97 | (&self.genome, &other.genome) 98 | } else { 99 | (&other.genome, &self.genome) 100 | }; 101 | 102 | Individual { 103 | genome: fitter.cross_in(weaker, rng), 104 | age: 0, 105 | behavior: None, 106 | fitness: None, 107 | novelty: None, 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/genes/mod.rs: -------------------------------------------------------------------------------- 1 | use rand::{prelude::IteratorRandom, prelude::SliceRandom, Rng}; 2 | use serde::{Deserialize, Serialize}; 3 | use std::{collections::HashSet, hash::Hash, iter::FromIterator, ops::Deref, ops::DerefMut}; 4 | 5 | pub mod activations; 6 | pub mod connections; 7 | mod id; 8 | pub mod nodes; 9 | mod weights; 10 | 11 | pub use activations::Activation; 12 | pub use id::{id_generator::IdGenerator, Id}; 13 | pub use weights::Weight; 14 | 15 | pub trait Gene: Eq + Hash {} 16 | 17 | impl> Gene for T {} 18 | #[derive(Debug, Clone, Deserialize, Serialize)] 19 | pub struct Genes(pub HashSet); 20 | 21 | impl Default for Genes { 22 | fn default() -> Self { 23 | Genes(Default::default()) 24 | } 25 | } 26 | 27 | impl Deref for Genes { 28 | type Target = HashSet; 29 | 30 | fn deref(&self) -> &Self::Target { 31 | &self.0 32 | } 33 | } 34 | 35 | impl DerefMut for Genes { 36 | fn deref_mut(&mut self) -> &mut Self::Target { 37 | &mut self.0 38 | } 39 | } 40 | 41 | impl Genes { 42 | pub fn iterate_with_random_offset(&self, rng: &mut impl Rng) -> impl Iterator { 43 | self.iter() 44 | .cycle() 45 | .skip((rng.gen::() * self.len() as f64).floor() as usize) 46 | .take(self.len()) 47 | } 48 | 49 | pub fn random(&self, rng: &mut impl Rng) -> Option<&T> { 50 | self.iter().choose(rng) 51 | } 52 | 53 | pub fn drain_into_random(&mut self, rng: &mut impl Rng) -> impl Iterator { 54 | let mut random_vec = self.drain().collect::>(); 55 | random_vec.shuffle(rng); 56 | random_vec.into_iter() 57 | } 58 | 59 | pub fn iterate_matches<'a>( 60 | &'a self, 61 | other: &'a Genes, 62 | ) -> impl Iterator { 63 | self.intersection(other) 64 | // we know item exists in other as we are iterating the intersection 65 | .map(move |item_self| (item_self, other.get(item_self).unwrap())) 66 | } 67 | 68 | pub fn iterate_unmatches<'a>(&'a self, other: &'a Genes) -> impl Iterator { 69 | self.symmetric_difference(other) 70 | } 71 | } 72 | 73 | impl FromIterator for Genes { 74 | fn from_iter>(iter: I) -> Self { 75 | Genes(iter.into_iter().collect()) 76 | } 77 | } 78 | 79 | impl<'a, U: 'a, T: Gene + Deref> Genes { 80 | pub fn iterate_unwrapped(&'a self) -> impl Iterator + Sized + Clone { 81 | self.iter().map(|value| value.deref()) 82 | } 83 | } 84 | 85 | impl> Genes { 86 | pub fn as_sorted_vec(&self) -> Vec<&U> { 87 | let mut vec: Vec<&U> = self.iterate_unwrapped().collect(); 88 | vec.sort_unstable(); 89 | vec 90 | } 91 | } 92 | 93 | impl Genes { 94 | pub fn cross_in(&self, other: &Self, rng: &mut impl Rng) -> Self { 95 | self.iterate_matches(other) 96 | .map(|(gene_self, gene_other)| { 97 | if rng.gen::() < 0.5 { 98 | gene_self.clone() 99 | } else { 100 | gene_other.clone() 101 | } 102 | }) 103 | .chain(self.difference(other).cloned()) 104 | .collect() 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/individual/behavior.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Deref; 2 | 3 | use ndarray::{Array2, ArrayView1, Axis}; 4 | use serde::{Deserialize, Serialize}; 5 | 6 | use crate::utility::gym::StandardScaler; 7 | 8 | #[derive(Debug, Default, Clone, Serialize, Deserialize)] 9 | pub struct Behavior(pub Vec); 10 | 11 | impl Deref for Behavior { 12 | type Target = Vec; 13 | 14 | fn deref(&self) -> &Self::Target { 15 | &self.0 16 | } 17 | } 18 | 19 | pub struct Behaviors<'a>(Vec<&'a Behavior>); 20 | 21 | impl<'a> Deref for Behaviors<'a> { 22 | type Target = Vec<&'a Behavior>; 23 | 24 | fn deref(&self) -> &Self::Target { 25 | &self.0 26 | } 27 | } 28 | 29 | impl<'a> From> for Behaviors<'a> { 30 | fn from(behaviors: Vec<&'a Behavior>) -> Self { 31 | Behaviors(behaviors) 32 | } 33 | } 34 | 35 | impl<'a> Behaviors<'a> { 36 | pub fn compute_novelty(&self, nearest_neighbors: usize) -> Vec { 37 | let width = self[0].len(); 38 | let height = self.len(); 39 | 40 | let mut behavior_iter = self.iter(); 41 | 42 | let mut behavior_arr: Array2 = Array2::zeros((width, height)); 43 | for mut row in behavior_arr.axis_iter_mut(Axis(1)) { 44 | row += &ArrayView1::from(behavior_iter.next().unwrap().as_slice()); 45 | } 46 | 47 | let standard_scaler = StandardScaler::new(behavior_arr.view().t()); 48 | 49 | let mut z_scores_arr: Array2 = Array2::zeros((width, height)); 50 | 51 | for (index, row) in behavior_arr.axis_iter(Axis(1)).enumerate() { 52 | let mut z_row = z_scores_arr.index_axis_mut(Axis(1), index); 53 | z_row += &standard_scaler.scale(row); 54 | } 55 | 56 | let mut raw_novelties = Vec::new(); 57 | 58 | for z_score in z_scores_arr.axis_iter(Axis(1)) { 59 | let mut distances = z_scores_arr 60 | .axis_iter(Axis(1)) 61 | // build euclidian distance to neighbor 62 | .map(|neighbor| { 63 | neighbor 64 | .iter() 65 | .zip(z_score.iter()) 66 | .map(|(n, z)| (n - z).powi(2)) 67 | .sum::() 68 | }) 69 | .map(|sum| sum.sqrt()) 70 | .collect::>(); 71 | 72 | distances.sort_by(|dist_0, dist_1| { 73 | dist_0 74 | .partial_cmp(&dist_1) 75 | .unwrap_or_else(|| panic!("failed to compare {} and {}", dist_0, dist_1)) 76 | }); 77 | 78 | // take k nearest neighbors, calculate and assign spareseness 79 | let sparseness = distances 80 | .iter() 81 | // skip self with zero distance 82 | .skip(1) 83 | .take(nearest_neighbors) 84 | .sum::() 85 | / nearest_neighbors as f64; 86 | 87 | raw_novelties.push(sparseness); 88 | } 89 | 90 | raw_novelties 91 | } 92 | } 93 | 94 | #[cfg(test)] 95 | mod tests { 96 | use super::{Behavior, Behaviors}; 97 | 98 | #[test] 99 | fn compute_z_score() { 100 | let behavior_a = Behavior(vec![0.0, 1.0, 2.0]); 101 | let behavior_b = Behavior(vec![2.0, 1.0, 0.0]); 102 | 103 | let behaviors = Behaviors(vec![&behavior_a, &behavior_b]); 104 | 105 | let novelty = behaviors.compute_novelty(1); 106 | 107 | dbg!(novelty); 108 | 109 | // assert_eq!(novelty, vec![]); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/individual/scores.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use std::ops::{Deref, DerefMut}; 3 | 4 | pub trait Score 5 | where 6 | Self: Sized, 7 | { 8 | type Value; 9 | fn value(&self) -> Self::Value; 10 | 11 | fn shift(self, baseline: Self::Value) -> Self; 12 | fn normalize_with(self, with: Self::Value) -> Self; 13 | } 14 | 15 | pub trait ScoreType {} 16 | 17 | pub trait ScoreValue { 18 | type Value; 19 | fn value(&self) -> Self::Value; 20 | } 21 | 22 | #[derive(Debug, Default, Copy, Clone, Deserialize, Serialize, PartialEq)] 23 | pub struct Fitness(pub f64); 24 | 25 | impl ScoreValue for Fitness { 26 | type Value = f64; 27 | 28 | fn value(&self) -> Self::Value { 29 | self.0 30 | } 31 | } 32 | 33 | #[derive(Debug, Default, Clone, Deserialize, Serialize)] 34 | pub struct FitnessScore { 35 | pub raw: Raw, 36 | pub shifted: Shifted, 37 | pub normalized: Normalized, 38 | } 39 | 40 | impl FitnessScore { 41 | pub fn new(raw: f64, baseline: f64, with: f64) -> Self { 42 | let raw = Raw::fitness(raw); 43 | let shifted = raw.shift(baseline); 44 | let normalized = shifted.normalize(with); 45 | Self { 46 | raw, 47 | shifted, 48 | normalized, 49 | } 50 | } 51 | } 52 | 53 | #[derive(Debug, Default, Copy, Clone, Deserialize, Serialize, PartialEq)] 54 | pub struct Novelty(f64); 55 | 56 | impl ScoreValue for Novelty { 57 | type Value = f64; 58 | 59 | fn value(&self) -> Self::Value { 60 | self.0 61 | } 62 | } 63 | 64 | #[derive(Debug, Default, Clone, Deserialize, Serialize)] 65 | pub struct NoveltyScore { 66 | pub raw: Raw, 67 | pub shifted: Shifted, 68 | pub normalized: Normalized, 69 | } 70 | 71 | impl NoveltyScore { 72 | pub fn new(raw: f64, baseline: f64, with: f64) -> Self { 73 | let raw = Raw::novelty(raw); 74 | let shifted = raw.shift(baseline); 75 | let normalized = shifted.normalize(with); 76 | Self { 77 | raw, 78 | shifted, 79 | normalized, 80 | } 81 | } 82 | } 83 | 84 | macro_rules! makeScoreType { 85 | ( $( $name:ident ),* ) => { 86 | $( 87 | #[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash, PartialOrd, Ord, Deserialize, Serialize)] 88 | pub struct $name(T); 89 | 90 | impl ScoreType for $name {} 91 | 92 | impl Deref for $name { 93 | type Target = T; 94 | 95 | fn deref(&self) -> &Self::Target { 96 | &self.0 97 | } 98 | } 99 | 100 | impl DerefMut for $name { 101 | fn deref_mut(&mut self) -> &mut Self::Target { 102 | &mut self.0 103 | } 104 | } 105 | )* 106 | }; 107 | } 108 | 109 | makeScoreType!(Raw, Normalized, Shifted); 110 | 111 | impl Raw { 112 | pub fn fitness(fitness: f64) -> Self { 113 | Self(Fitness(fitness)) 114 | } 115 | pub fn shift(self, baseline: f64) -> Shifted { 116 | Shifted(Fitness(self.value() - baseline)) 117 | } 118 | } 119 | 120 | impl Shifted { 121 | pub fn normalize(self, with: f64) -> Normalized { 122 | Normalized(Fitness(self.value() / with.max(1.0))) 123 | } 124 | } 125 | 126 | impl Raw { 127 | pub fn novelty(novelty: f64) -> Self { 128 | Self(Novelty(novelty)) 129 | } 130 | pub fn shift(self, baseline: f64) -> Shifted { 131 | Shifted(Novelty(self.value() - baseline)) 132 | } 133 | } 134 | 135 | impl Shifted { 136 | pub fn normalize(self, with: f64) -> Normalized { 137 | Normalized(Novelty(self.value() / with.max(1.0))) 138 | } 139 | } 140 | 141 | #[cfg(test)] 142 | mod tests { 143 | use super::{Fitness, Normalized, Raw, Shifted}; 144 | 145 | #[test] 146 | fn shift_raw() { 147 | let raw = Raw::fitness(1.0); 148 | 149 | let shifted = raw.shift(-2.0); 150 | 151 | assert_eq!(shifted, Shifted(Fitness(3.0))) 152 | } 153 | 154 | #[test] 155 | fn normalize_shifted() { 156 | let shifted = Shifted(Fitness(1.0)); 157 | 158 | let normalized = shifted.normalize(2.0); 159 | 160 | assert_eq!(normalized, Normalized(Fitness(0.5))) 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/utility/favannat_impl.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use favannat::network::{EdgeLike, NetLike, NodeLike, Recurrent}; 4 | 5 | use crate::{ 6 | genes::{ 7 | activations, 8 | connections::{Connection, FeedForward}, 9 | nodes::{Input, Node, Output}, 10 | Activation, Id, Weight, 11 | }, 12 | Individual, 13 | }; 14 | 15 | impl NodeLike for Node { 16 | fn id(&self) -> usize { 17 | self.id().0 18 | } 19 | fn activation(&self) -> fn(f64) -> f64 { 20 | match self.1 { 21 | Activation::Linear => activations::LINEAR, 22 | Activation::Sigmoid => activations::SIGMOID, 23 | Activation::Gaussian => activations::GAUSSIAN, 24 | Activation::Tanh => activations::TANH, 25 | Activation::Step => activations::STEP, 26 | Activation::Sine => activations::SINE, 27 | Activation::Cosine => activations::COSINE, 28 | Activation::Inverse => activations::INVERSE, 29 | Activation::Absolute => activations::ABSOLUTE, 30 | Activation::Relu => activations::RELU, 31 | Activation::Squared => activations::SQUARED, 32 | } 33 | } 34 | } 35 | 36 | impl EdgeLike for Connection { 37 | fn start(&self) -> usize { 38 | self.input().0 39 | } 40 | fn end(&self) -> usize { 41 | self.output().0 42 | } 43 | fn weight(&self) -> f64 { 44 | (self.1).0 45 | } 46 | } 47 | 48 | impl NetLike for Individual { 49 | fn nodes(&self) -> Vec<&Node> { 50 | self.genome.nodes().collect() 51 | } 52 | fn edges(&self) -> Vec<&Connection> { 53 | self.feed_forward.as_sorted_vec() 54 | } 55 | fn inputs(&self) -> Vec<&Node> { 56 | self.inputs.as_sorted_vec() 57 | } 58 | fn outputs(&self) -> Vec<&Node> { 59 | self.outputs.as_sorted_vec() 60 | } 61 | } 62 | 63 | impl Recurrent for Individual { 64 | type Net = Self; 65 | 66 | fn unroll(&self) -> Self::Net { 67 | let mut unrolled_genome = self.clone(); 68 | 69 | // maps recurrent connection input to wrapped actual input 70 | let mut unroll_map: HashMap = HashMap::new(); 71 | let mut tmp_ids = (0..usize::MAX).rev(); 72 | 73 | for recurrent_connection in self.recurrent.as_sorted_vec() { 74 | let recurrent_input = unroll_map 75 | .entry(recurrent_connection.input()) 76 | .or_insert_with(|| { 77 | let wrapper_input_id = Id(tmp_ids.next().unwrap()); 78 | 79 | let wrapper_input_node = Input(Node(wrapper_input_id, Activation::Linear)); 80 | let wrapper_output_node = 81 | Output(Node(Id(tmp_ids.next().unwrap()), Activation::Linear)); 82 | 83 | // used to carry value into next evaluation 84 | let outward_wrapping_connection = FeedForward(Connection( 85 | recurrent_connection.input(), 86 | Weight(1.0), 87 | Node::id(&*wrapper_output_node), 88 | )); 89 | 90 | // add nodes for wrapping 91 | unrolled_genome.inputs.insert(wrapper_input_node); 92 | unrolled_genome.outputs.insert(wrapper_output_node); 93 | 94 | // add outward wrapping connection 95 | unrolled_genome 96 | .feed_forward 97 | .insert(outward_wrapping_connection); 98 | 99 | wrapper_input_id 100 | }); 101 | 102 | let inward_wrapping_connection = FeedForward(Connection( 103 | *recurrent_input, 104 | recurrent_connection.1, 105 | recurrent_connection.output(), 106 | )); 107 | 108 | unrolled_genome 109 | .feed_forward 110 | .insert(inward_wrapping_connection); 111 | } 112 | unrolled_genome 113 | } 114 | 115 | fn recurrent_edges(&self) -> Vec<&Connection> { 116 | self.recurrent.as_sorted_vec() 117 | } 118 | } 119 | 120 | #[cfg(test)] 121 | mod tests { 122 | use favannat::network::Recurrent; 123 | 124 | use crate::{Individual, Parameters}; 125 | 126 | #[test] 127 | fn unroll_genome() { 128 | todo!("update to individual"); 129 | /* let mut parameters: Parameters = Default::default(); 130 | parameters.mutation.weights.perturbation_std_dev = 1.0; 131 | 132 | parameters.setup.dimension.input = 1; 133 | parameters.setup.dimension.output = 1; 134 | parameters.mutation.recurrent = 1.0; 135 | 136 | let mut genome_0 = Genome::new(&mut context, ¶meters); 137 | 138 | genome_0.init(&mut context, ¶meters); 139 | 140 | // should add recurrent connection from input to output 141 | assert!(genome_0.add_connection(&mut context, ¶meters).is_ok()); 142 | // dont add same connection twice 143 | assert!(genome_0.add_connection(&mut context, ¶meters).is_err()); 144 | 145 | assert_eq!(genome_0.recurrent.len(), 1); 146 | 147 | let genome_1 = genome_0.unroll(); 148 | 149 | assert_eq!(genome_1.hidden.len(), 2); 150 | assert_eq!(genome_1.feed_forward.len(), 3); */ 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/population.rs: -------------------------------------------------------------------------------- 1 | use std::time::Instant; 2 | 3 | use rand::prelude::SliceRandom; 4 | 5 | use crate::{ 6 | genes::IdGenerator, 7 | individual::{ 8 | behavior::{Behavior, Behaviors}, 9 | scores::{Fitness, FitnessScore, NoveltyScore, Raw, ScoreValue}, 10 | Individual, 11 | }, 12 | parameters::Parameters, 13 | runtime::progress::Progress, 14 | utility::{rng::NeatRng, statistics::PopulationStatistics}, 15 | }; 16 | 17 | pub struct Population { 18 | individuals: Vec, 19 | archive: Vec, 20 | population_statistics: PopulationStatistics, 21 | rng: NeatRng, 22 | id_gen: IdGenerator, 23 | } 24 | 25 | impl Population { 26 | pub fn new(parameters: &Parameters) -> Self { 27 | // create id book-keeping 28 | let mut id_gen = IdGenerator::default(); 29 | 30 | // generate genome with initial ids for structure 31 | let initial_individual = Individual::initial(&mut id_gen, parameters); 32 | 33 | // create randomn source 34 | let mut rng = NeatRng::new( 35 | parameters.setup.seed, 36 | parameters.mutation.weight_perturbation_std_dev, 37 | ); 38 | 39 | let mut individuals = Vec::new(); 40 | 41 | // generate initial, mutated individuals 42 | for _ in 0..parameters.setup.population_size { 43 | let mut other_genome = initial_individual.clone(); 44 | other_genome.init(&mut rng, parameters); 45 | other_genome.mutate(&mut rng, &mut id_gen, parameters); 46 | individuals.push(other_genome); 47 | } 48 | 49 | Population { 50 | individuals, 51 | archive: Vec::new(), 52 | rng, 53 | id_gen, 54 | population_statistics: PopulationStatistics::default(), 55 | } 56 | } 57 | 58 | pub fn individuals(&self) -> &Vec { 59 | &self.individuals 60 | } 61 | 62 | fn generate_offspring(&mut self, parameters: &Parameters) { 63 | let now = Instant::now(); 64 | 65 | let partners = self.individuals.as_slice(); 66 | 67 | let mut scores: Vec = self 68 | .individuals 69 | .iter() 70 | .map(|individual| individual.score()) 71 | .collect(); 72 | 73 | let mut minimum_score = f64::INFINITY; 74 | let mut maximum_score = f64::NEG_INFINITY; 75 | 76 | // analyse score values 77 | for &score in &scores { 78 | if score > maximum_score { 79 | maximum_score = score; 80 | } 81 | if score < minimum_score { 82 | minimum_score = score; 83 | } 84 | } 85 | 86 | // shift and normalize scores 87 | for score in &mut scores { 88 | *score -= minimum_score; 89 | *score /= maximum_score; 90 | } 91 | 92 | let total_score: f64 = scores.iter().sum(); 93 | 94 | let offspring_count = parameters.setup.population_size - self.individuals.len(); 95 | 96 | let score_offspring_value = offspring_count as f64 / total_score; 97 | 98 | let mut offsprings = Vec::new(); 99 | 100 | for (parent_index, score) in scores.iter().enumerate() { 101 | for _ in 0..(score * score_offspring_value).round() as usize { 102 | let mut offspring = self.individuals[parent_index].crossover( 103 | partners 104 | .choose(&mut self.rng.small) 105 | .expect("could not select random partner"), 106 | &mut self.rng.small, 107 | ); 108 | offspring.mutate(&mut self.rng, &mut self.id_gen, parameters); 109 | offsprings.push(offspring); 110 | } 111 | } 112 | 113 | /* // generate as many offspring as population size allows 114 | for parent in self 115 | .individuals 116 | .iter() 117 | .cycle() 118 | .take(parameters.setup.population_size - self.individuals.len()) 119 | { 120 | let mut offspring = parent.crossover( 121 | partners 122 | .choose(&mut self.rng.small) 123 | .expect("could not select random partner"), 124 | &mut self.rng.small, 125 | ); 126 | offspring.mutate(&mut self.rng, &mut self.id_gen, parameters); 127 | offsprings.push(offspring); 128 | } */ 129 | 130 | self.individuals.extend(offsprings.into_iter()); 131 | 132 | // mutate entire population here ? 133 | 134 | self.population_statistics.milliseconds_elapsed_reproducing = now.elapsed().as_millis(); 135 | } 136 | 137 | fn calculate_novelty(&mut self, parameters: &Parameters) { 138 | let behaviors: Behaviors = self 139 | .individuals 140 | .iter() 141 | .flat_map(|individual| individual.behavior.as_ref()) 142 | .chain( 143 | self.archive 144 | .iter() 145 | .flat_map(|archived_individual| archived_individual.behavior.as_ref()), 146 | ) 147 | .collect::>() 148 | .into(); 149 | 150 | let behavior_count = behaviors.len() as f64; 151 | 152 | let raw_novelties = behaviors.compute_novelty(parameters.setup.novelty_nearest_neighbors); 153 | 154 | let most_novel = raw_novelties 155 | .iter() 156 | .enumerate() 157 | .take(self.individuals.len()) 158 | .max_by(|(_, a), (_, b)| a.partial_cmp(b).expect("could not compare floats")) 159 | .map(|(index, _)| index) 160 | .expect("failed finding most novel"); 161 | 162 | // add most novel individual to archive 163 | self.archive.push(self.individuals[most_novel].clone()); 164 | 165 | let mut raw_minimum = f64::INFINITY; 166 | let mut raw_sum = 0.0; 167 | let mut raw_maximum = f64::NEG_INFINITY; 168 | 169 | // analyse raw novelty values 170 | for &novelty in &raw_novelties { 171 | if novelty > raw_maximum { 172 | raw_maximum = novelty; 173 | } 174 | if novelty < raw_minimum { 175 | raw_minimum = novelty; 176 | } 177 | raw_sum += novelty; 178 | } 179 | 180 | let raw_minimum = Raw::novelty(raw_minimum); 181 | let raw_average = Raw::novelty(raw_sum / behavior_count); 182 | let raw_maximum = Raw::novelty(raw_maximum); 183 | 184 | let baseline = raw_minimum.value(); 185 | 186 | let shifted_minimum = raw_minimum.shift(baseline); 187 | let shifted_average = raw_average.shift(baseline); 188 | let shifted_maximum = raw_maximum.shift(baseline); 189 | 190 | let with = shifted_maximum.value(); 191 | 192 | let normalized_minimum = shifted_minimum.normalize(with); 193 | let normalized_average = shifted_average.normalize(with); 194 | let normalized_maximum = shifted_maximum.normalize(with); 195 | 196 | for (index, individual) in self.individuals.iter_mut().enumerate() { 197 | individual.novelty = Some(NoveltyScore::new(raw_novelties[index], baseline, with)); 198 | } 199 | 200 | self.population_statistics.novelty.raw_maximum = raw_maximum.value(); 201 | self.population_statistics.novelty.raw_minimum = raw_minimum.value(); 202 | self.population_statistics.novelty.raw_average = raw_average.value(); 203 | 204 | self.population_statistics.novelty.shifted_maximum = shifted_maximum.value(); 205 | self.population_statistics.novelty.shifted_minimum = shifted_minimum.value(); 206 | self.population_statistics.novelty.shifted_average = shifted_average.value(); 207 | 208 | self.population_statistics.novelty.normalized_maximum = normalized_maximum.value(); 209 | self.population_statistics.novelty.normalized_minimum = normalized_minimum.value(); 210 | self.population_statistics.novelty.normalized_average = normalized_average.value(); 211 | } 212 | 213 | fn assign_behavior(&mut self, progress: &[Progress]) { 214 | let behaviors: Vec<(usize, &Behavior)> = progress 215 | .iter() 216 | .enumerate() 217 | .flat_map(|(index, progress)| progress.behavior().map(|raw| (index, raw))) 218 | .collect(); 219 | 220 | if behaviors.is_empty() { 221 | return; 222 | } 223 | 224 | for (index, behavior) in behaviors { 225 | self.individuals[index].behavior = Some(behavior.clone()); 226 | } 227 | } 228 | 229 | fn assign_fitness(&mut self, progress: &[Progress]) { 230 | let fitnesses: Vec<(usize, Raw)> = progress 231 | .iter() 232 | .enumerate() 233 | .flat_map(|(index, progress)| progress.raw_fitness().map(|raw| (index, raw))) 234 | .collect(); 235 | 236 | if fitnesses.is_empty() { 237 | return; 238 | } 239 | 240 | let mut raw_minimum = f64::INFINITY; 241 | let mut raw_sum = 0.0; 242 | let mut raw_maximum = f64::NEG_INFINITY; 243 | 244 | // analyse raw fitness values 245 | for (_, raw_fitness) in &fitnesses { 246 | if raw_fitness.value() > raw_maximum { 247 | raw_maximum = raw_fitness.value(); 248 | } 249 | if raw_fitness.value() < raw_minimum { 250 | raw_minimum = raw_fitness.value(); 251 | } 252 | raw_sum += raw_fitness.value(); 253 | } 254 | 255 | let raw_minimum = Raw::fitness(raw_minimum); 256 | let raw_average = Raw::fitness(raw_sum / fitnesses.len() as f64); 257 | let raw_maximum = Raw::fitness(raw_maximum); 258 | 259 | let baseline = raw_minimum.value(); 260 | 261 | let shifted_minimum = raw_minimum.shift(baseline); 262 | let shifted_average = raw_average.shift(baseline); 263 | let shifted_maximum = raw_maximum.shift(baseline); 264 | 265 | let with = shifted_maximum.value(); 266 | 267 | let normalized_minimum = shifted_minimum.normalize(with); 268 | let normalized_average = shifted_average.normalize(with); 269 | let normalized_maximum = shifted_maximum.normalize(with); 270 | 271 | // shift and normalize fitness 272 | for (index, raw_fitness) in fitnesses { 273 | self.individuals[index].fitness = 274 | Some(FitnessScore::new(raw_fitness.value(), baseline, with)); 275 | } 276 | 277 | self.population_statistics.fitness.raw_maximum = raw_maximum.value(); 278 | self.population_statistics.fitness.raw_minimum = raw_minimum.value(); 279 | self.population_statistics.fitness.raw_average = raw_average.value(); 280 | 281 | self.population_statistics.fitness.shifted_maximum = shifted_maximum.value(); 282 | self.population_statistics.fitness.shifted_minimum = shifted_minimum.value(); 283 | self.population_statistics.fitness.shifted_average = shifted_average.value(); 284 | 285 | self.population_statistics.fitness.normalized_maximum = normalized_maximum.value(); 286 | self.population_statistics.fitness.normalized_minimum = normalized_minimum.value(); 287 | self.population_statistics.fitness.normalized_average = normalized_average.value(); 288 | } 289 | 290 | fn top_fitness_performer(&mut self) -> Individual { 291 | self.individuals.sort_by(|individual_0, individual_1| { 292 | individual_1 293 | .fitness 294 | .as_ref() 295 | .map(|f| f.normalized.value()) 296 | .unwrap_or(f64::NEG_INFINITY) 297 | .partial_cmp( 298 | &individual_0 299 | .fitness 300 | .as_ref() 301 | .map(|f| f.normalized.value()) 302 | .unwrap_or(f64::NEG_INFINITY), 303 | ) 304 | .unwrap_or_else(|| { 305 | panic!( 306 | "failed to compare fitness {} and fitness {}", 307 | individual_0 308 | .fitness 309 | .as_ref() 310 | .map(|f| f.normalized.value()) 311 | .unwrap_or(f64::NEG_INFINITY), 312 | individual_1 313 | .fitness 314 | .as_ref() 315 | .map(|f| f.normalized.value()) 316 | .unwrap_or(f64::NEG_INFINITY) 317 | ) 318 | }) 319 | }); 320 | 321 | self.individuals 322 | .first() 323 | .expect("individuals are empty!") 324 | .clone() 325 | } 326 | 327 | fn sort_individuals_by_score(&mut self) { 328 | // sort individuals by their score (descending, i.e. highest score first) 329 | self.individuals.sort_by(|individual_0, individual_1| { 330 | individual_1 331 | .score() 332 | .partial_cmp(&individual_0.score()) 333 | .unwrap_or_else(|| { 334 | panic!( 335 | "failed to compare score {} and score {}", 336 | individual_0.score(), 337 | individual_1.score() 338 | ) 339 | }) 340 | }); 341 | } 342 | 343 | pub fn next_generation( 344 | &mut self, 345 | parameters: &Parameters, 346 | progress: &[Progress], 347 | ) -> PopulationStatistics { 348 | self.assign_fitness(progress); 349 | self.assign_behavior(progress); 350 | // calculate novelty based on previously assigned behavior 351 | self.calculate_novelty(parameters); 352 | 353 | self.sort_individuals_by_score(); 354 | 355 | // remove any individual that does not survive 356 | self.individuals.truncate( 357 | (parameters.setup.population_size as f64 * parameters.setup.survival_rate).ceil() 358 | as usize, 359 | ); 360 | 361 | // increment age of surviving individuals 362 | for individual in &mut self.individuals { 363 | individual.age += 1; 364 | } 365 | 366 | // reproduce from surviving individuals 367 | self.generate_offspring(parameters); 368 | 369 | // return some statistics 370 | self.gather_statistics() 371 | } 372 | 373 | fn gather_statistics(&mut self) -> PopulationStatistics { 374 | self.population_statistics.top_performer = self.top_fitness_performer(); 375 | 376 | // determine maximum age 377 | self.population_statistics.age_maximum = self 378 | .individuals 379 | .iter() 380 | .map(|individual| individual.age) 381 | .max() 382 | .expect("cant find max age"); 383 | 384 | // determine average age 385 | self.population_statistics.age_average = self 386 | .individuals 387 | .iter() 388 | .map(|individual| individual.age as f64) 389 | .sum::() 390 | / self.individuals.len() as f64; 391 | 392 | self.population_statistics.clone() 393 | } 394 | } 395 | -------------------------------------------------------------------------------- /src/individual/genome.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | genes::{ 3 | connections::{Connection, FeedForward, Recurrent}, 4 | nodes::{Hidden, Input, Node, Output}, 5 | Activation, Genes, IdGenerator, Weight, 6 | }, 7 | parameters::Parameters, 8 | utility::rng::NeatRng, 9 | }; 10 | 11 | use rand::{ 12 | prelude::{IteratorRandom, SliceRandom}, 13 | Rng, 14 | }; 15 | use serde::{Deserialize, Serialize}; 16 | 17 | #[derive(Debug, Default, Clone, Serialize, Deserialize)] 18 | pub struct Genome { 19 | pub inputs: Genes>, 20 | pub hidden: Genes>, 21 | pub outputs: Genes>, 22 | pub feed_forward: Genes>, 23 | pub recurrent: Genes>, 24 | } 25 | 26 | impl Genome { 27 | pub fn new(id_gen: &mut IdGenerator, parameters: &Parameters) -> Self { 28 | Genome { 29 | inputs: (0..parameters.setup.input_dimension) 30 | .map(|_| Input(Node(id_gen.next_id(), Activation::Linear))) 31 | .collect(), 32 | outputs: (0..parameters.setup.output_dimension) 33 | .map(|_| Output(Node(id_gen.next_id(), parameters.activations.output_nodes))) 34 | .collect(), 35 | ..Default::default() 36 | } 37 | } 38 | 39 | pub fn nodes(&self) -> impl Iterator { 40 | self.inputs 41 | .iterate_unwrapped() 42 | .chain(self.hidden.iterate_unwrapped()) 43 | .chain(self.outputs.iterate_unwrapped()) 44 | } 45 | 46 | pub fn init(&mut self, rng: &mut NeatRng, parameters: &Parameters) { 47 | for input in self.inputs.iterate_with_random_offset(&mut rng.small).take( 48 | (rng.small.gen::() * parameters.setup.input_dimension as f64).ceil() as usize, 49 | ) { 50 | // connect to every output 51 | for output in self.outputs.iter() { 52 | assert!(self.feed_forward.insert(FeedForward(Connection( 53 | input.id(), 54 | Weight(rng.weight_perturbation()), 55 | output.id() 56 | )))); 57 | } 58 | } 59 | } 60 | 61 | pub fn len(&self) -> usize { 62 | self.feed_forward.len() + self.recurrent.len() 63 | } 64 | 65 | pub fn is_empty(&self) -> bool { 66 | self.feed_forward.is_empty() && self.recurrent.is_empty() 67 | } 68 | 69 | pub fn cross_in(&self, other: &Self, rng: &mut impl Rng) -> Self { 70 | let feed_forward = self.feed_forward.cross_in(&other.feed_forward, rng); 71 | 72 | let recurrent = self.recurrent.cross_in(&other.recurrent, rng); 73 | 74 | let hidden = self.hidden.cross_in(&other.hidden, rng); 75 | 76 | Genome { 77 | feed_forward, 78 | recurrent, 79 | hidden, 80 | // use input and outputs from fitter, but they should be identical with weaker 81 | inputs: self.inputs.clone(), 82 | outputs: self.outputs.clone(), 83 | } 84 | } 85 | 86 | pub fn mutate(&mut self, rng: &mut NeatRng, id_gen: &mut IdGenerator, parameters: &Parameters) { 87 | // mutate weigths 88 | // if context.gamble(parameters.mutation.weight) { 89 | self.change_weights(rng); 90 | // } 91 | 92 | // mutate connection gene 93 | if rng.gamble(parameters.mutation.new_connection_chance) { 94 | self.add_connection(rng, parameters).unwrap_or_default(); 95 | } 96 | 97 | // mutate node gene 98 | if rng.gamble(parameters.mutation.new_node_chance) { 99 | self.add_node(rng, id_gen, parameters); 100 | } 101 | 102 | // change some activation 103 | if rng.gamble(parameters.mutation.change_activation_function_chance) { 104 | self.alter_activation(rng, parameters); 105 | } 106 | } 107 | 108 | pub fn change_weights(&mut self, rng: &mut NeatRng) { 109 | self.feed_forward = self 110 | .feed_forward 111 | .drain_into_random(&mut rng.small) 112 | .map(|mut connection| { 113 | connection.adjust_weight(rng.weight_perturbation()); 114 | connection 115 | }) 116 | .collect(); 117 | 118 | self.recurrent = self 119 | .recurrent 120 | .drain_into_random(&mut rng.small) 121 | .map(|mut connection| { 122 | connection.adjust_weight(rng.weight_perturbation()); 123 | connection 124 | }) 125 | .collect(); 126 | } 127 | 128 | pub fn alter_activation(&mut self, rng: &mut NeatRng, parameters: &Parameters) { 129 | if let Some(node) = self.hidden.random(&mut rng.small) { 130 | let updated = Hidden(Node( 131 | node.id(), 132 | parameters 133 | .activations 134 | .hidden_nodes 135 | .iter() 136 | .filter(|&&activation| activation != node.1) 137 | .choose(&mut rng.small) 138 | .cloned() 139 | .unwrap_or(node.1), 140 | )); 141 | 142 | self.hidden.replace(updated); 143 | } 144 | } 145 | 146 | pub fn add_node( 147 | &mut self, 148 | rng: &mut NeatRng, 149 | id_gen: &mut IdGenerator, 150 | parameters: &Parameters, 151 | ) { 152 | // select an connection gene and split 153 | let mut random_connection = self.feed_forward.random(&mut rng.small).cloned().unwrap(); 154 | 155 | let id = id_gen 156 | .cached_id_iter(random_connection.id()) 157 | .find(|&id| { 158 | self.hidden 159 | .get(&Hidden(Node(id, Activation::Linear))) 160 | .is_none() 161 | }) 162 | .unwrap(); 163 | 164 | // construct new node gene 165 | let new_node = Hidden(Node( 166 | id, 167 | parameters 168 | .activations 169 | .hidden_nodes 170 | .choose(&mut rng.small) 171 | .cloned() 172 | .unwrap(), 173 | )); 174 | 175 | // insert new connection pointing to new node 176 | assert!(self.feed_forward.insert(FeedForward(Connection( 177 | random_connection.input(), 178 | Weight(1.0), 179 | new_node.id(), 180 | )))); 181 | // insert new connection pointing from new node 182 | assert!(self.feed_forward.insert(FeedForward(Connection( 183 | new_node.id(), 184 | random_connection.1, 185 | random_connection.output(), 186 | )))); 187 | // insert new node into genome 188 | assert!(self.hidden.insert(new_node)); 189 | 190 | // update weight to zero to 'deactivate' connnection 191 | random_connection.1 = Weight(0.0); 192 | self.feed_forward.replace(random_connection); 193 | } 194 | 195 | pub fn add_connection( 196 | &mut self, 197 | rng: &mut NeatRng, 198 | parameters: &Parameters, 199 | ) -> Result<(), &'static str> { 200 | let is_recurrent = rng.gamble(parameters.mutation.connection_is_recurrent_chance); 201 | 202 | let start_node_iterator = self 203 | .inputs 204 | .iterate_unwrapped() 205 | .chain(self.hidden.iterate_unwrapped()); 206 | 207 | let end_node_iterator = self 208 | .hidden 209 | .iterate_unwrapped() 210 | .chain(self.outputs.iterate_unwrapped()); 211 | 212 | for start_node in start_node_iterator 213 | // make iterator wrap 214 | .cycle() 215 | // randomly offset into the iterator to choose any node 216 | .skip( 217 | (rng.small.gen::() * (self.inputs.len() + self.hidden.len()) as f64).floor() 218 | as usize, 219 | ) 220 | // just loop every value once 221 | .take(self.inputs.len() + self.hidden.len()) 222 | { 223 | if let Some(end_node) = end_node_iterator.clone().find(|&end_node| { 224 | end_node != start_node 225 | && !self.are_connected(&start_node, end_node, is_recurrent) 226 | && (is_recurrent || !self.would_form_cycle(start_node, end_node)) 227 | }) { 228 | if is_recurrent { 229 | assert!(self.recurrent.insert(Recurrent(Connection( 230 | start_node.id(), 231 | Weight(rng.weight_perturbation()), 232 | end_node.id(), 233 | )))); 234 | } else { 235 | // add new feed-forward connection 236 | assert!(self.feed_forward.insert(FeedForward(Connection( 237 | start_node.id(), 238 | Weight(rng.weight_perturbation()), 239 | end_node.id(), 240 | )))); 241 | } 242 | return Ok(()); 243 | } 244 | // no possible connection end present 245 | } 246 | Err("no connection possible") 247 | } 248 | 249 | // check if to nodes are connected 250 | fn are_connected(&self, start_node: &Node, end_node: &Node, recurrent: bool) -> bool { 251 | if recurrent { 252 | self.recurrent.contains(&Recurrent(Connection( 253 | start_node.id(), 254 | Weight(0.0), 255 | end_node.id(), 256 | ))) 257 | } else { 258 | self.feed_forward.contains(&FeedForward(Connection( 259 | start_node.id(), 260 | Weight(0.0), 261 | end_node.id(), 262 | ))) 263 | } 264 | } 265 | 266 | // can only operate when no cycles present yet, which is assumed 267 | fn would_form_cycle(&self, start_node: &Node, end_node: &Node) -> bool { 268 | // needs to detect if there is a path from end to start 269 | let mut possible_paths: Vec<&FeedForward> = self 270 | .feed_forward 271 | .iter() 272 | .filter(|connection| connection.input() == end_node.id()) 273 | .collect(); 274 | let mut next_possible_path = Vec::new(); 275 | 276 | while !possible_paths.is_empty() { 277 | for path in possible_paths { 278 | // we have a cycle if path leads to start_node_gene 279 | if path.output() == start_node.id() { 280 | return true; 281 | } 282 | // collect further paths 283 | else { 284 | next_possible_path.extend( 285 | self.feed_forward 286 | .iter() 287 | .filter(|connection| connection.input() == path.output()), 288 | ); 289 | } 290 | } 291 | possible_paths = next_possible_path; 292 | next_possible_path = Vec::new(); 293 | } 294 | false 295 | } 296 | } 297 | 298 | #[cfg(test)] 299 | mod tests { 300 | /* use super::Genome; 301 | use crate::{ 302 | context::{rng::NeatRng, Context}, 303 | genes::{ 304 | connections::{Connection, FeedForward}, 305 | nodes::{Hidden, Input, Node, Output}, 306 | Genes, 307 | }, 308 | }; 309 | use crate::{ 310 | genes::{Activation, Id, Weight}, 311 | parameters::Parameters, 312 | }; 313 | 314 | #[test] 315 | fn alter_activation() { 316 | let mut parameters: Parameters = Default::default(); 317 | parameters.mutation.weights.perturbation_range = 1.0; 318 | let mut context = Context::new(¶meters); 319 | 320 | parameters.setup.dimension.input = 1; 321 | parameters.setup.dimension.output = 1; 322 | parameters.initialization.connections = 1.0; 323 | parameters.initialization.activations = vec![Activation::Absolute, Activation::Cosine]; 324 | 325 | let mut genome = Genome::new(&mut context, ¶meters); 326 | 327 | genome.init(&mut context, ¶meters); 328 | 329 | genome.add_node(&mut context, ¶meters); 330 | 331 | let old_activation = genome.hidden.iter().next().unwrap().1; 332 | 333 | genome.alter_activation(&mut context, ¶meters); 334 | 335 | assert_ne!(genome.hidden.iter().next().unwrap().1, old_activation); 336 | } 337 | 338 | #[test] 339 | fn add_random_connection() { 340 | let mut parameters: Parameters = Default::default(); 341 | parameters.mutation.weights.perturbation_range = 1.0; 342 | let mut context = Context::new(¶meters); 343 | 344 | parameters.setup.dimension.input = 1; 345 | parameters.setup.dimension.output = 1; 346 | 347 | let mut genome = Genome::new(&mut context, ¶meters); 348 | 349 | let result = genome.add_connection(&mut context, ¶meters).is_ok(); 350 | 351 | println!("{:?}", genome); 352 | 353 | assert_eq!(result, true); 354 | assert_eq!(genome.feed_forward.len(), 1); 355 | } 356 | 357 | #[test] 358 | fn dont_add_same_connection_twice() { 359 | let mut parameters: Parameters = Default::default(); 360 | parameters.mutation.weights.perturbation_range = 1.0; 361 | let mut context = Context::new(¶meters); 362 | 363 | parameters.setup.dimension.input = 1; 364 | parameters.setup.dimension.output = 1; 365 | 366 | let mut genome = Genome::new(&mut context, ¶meters); 367 | 368 | let result_0 = genome.add_connection(&mut context, ¶meters).is_ok(); 369 | if let Err(message) = genome.add_connection(&mut context, ¶meters) { 370 | assert_eq!(message, "no connection possible"); 371 | } else { 372 | // assert!(false); 373 | unreachable!() 374 | } 375 | 376 | println!("{:?}", genome); 377 | 378 | assert_eq!(result_0, true); 379 | assert_eq!(genome.feed_forward.len(), 1); 380 | } 381 | 382 | #[test] 383 | fn add_random_node() { 384 | let mut parameters: Parameters = Default::default(); 385 | parameters.mutation.weights.perturbation_range = 1.0; 386 | parameters.initialization.activations = vec![Activation::Tanh]; 387 | let mut context = Context::new(¶meters); 388 | 389 | parameters.setup.dimension.input = 1; 390 | parameters.setup.dimension.output = 1; 391 | parameters.initialization.connections = 1.0; 392 | 393 | let mut genome = Genome::new(&mut context, ¶meters); 394 | 395 | genome.init(&mut context, ¶meters); 396 | genome.add_node(&mut context, ¶meters); 397 | 398 | println!("{:?}", genome); 399 | 400 | assert_eq!(genome.feed_forward.len(), 3); 401 | } 402 | 403 | #[test] 404 | fn crossover_same_fitness() { 405 | let mut parameters: Parameters = Default::default(); 406 | parameters.mutation.weights.perturbation_range = 1.0; 407 | parameters.initialization.activations = vec![Activation::Tanh]; 408 | let mut context = Context::new(¶meters); 409 | 410 | parameters.setup.dimension.input = 1; 411 | parameters.setup.dimension.output = 1; 412 | parameters.initialization.connections = 1.0; 413 | 414 | let mut genome_0 = Genome::new(&mut context, ¶meters); 415 | 416 | genome_0.init(&mut context, ¶meters); 417 | 418 | let mut genome_1 = genome_0.clone(); 419 | 420 | // mutate genome_0 421 | genome_0.add_node(&mut context, ¶meters); 422 | 423 | // mutate genome_1 424 | genome_1.add_node(&mut context, ¶meters); 425 | genome_1.add_node(&mut context, ¶meters); 426 | 427 | println!("genome_0 {:?}", genome_0); 428 | println!("genome_1 {:?}", genome_1); 429 | 430 | // shorter genome is fitter genome 431 | let offspring = genome_0.cross_in(&genome_1, &mut context); 432 | 433 | println!("offspring {:?}", offspring); 434 | 435 | assert_eq!(offspring.hidden.len(), 1); 436 | assert_eq!(offspring.feed_forward.len(), 3); 437 | } 438 | 439 | #[test] 440 | fn crossover_different_fitness_by_fitter() { 441 | todo!("move to individual/mod.rs"); 442 | 443 | let mut parameters: Parameters = Default::default(); 444 | parameters.mutation.weights.perturbation_range = 1.0; 445 | parameters.initialization.activations = vec![Activation::Tanh]; 446 | let mut context = Context::new(¶meters); 447 | 448 | parameters.setup.dimension.input = 2; 449 | parameters.setup.dimension.output = 1; 450 | parameters.initialization.connections = 1.0; 451 | 452 | let mut genome_0 = Genome::new(&mut context, ¶meters); 453 | 454 | genome_0.init(&mut context, ¶meters); 455 | 456 | let mut genome_1 = genome_0.clone(); 457 | 458 | /* genome_1.fitness.raw = Raw::fitness(1.0); 459 | genome_1.fitness.shifted = genome_1.fitness.raw.shift(0.0); 460 | genome_1.fitness.normalized = genome_1.fitness.shifted.normalize(1.0); */ 461 | 462 | // mutate genome_0 463 | genome_0.add_node(&mut context, ¶meters); 464 | 465 | // mutate genome_1 466 | genome_1.add_node(&mut context, ¶meters); 467 | genome_1.add_connection(&mut context, ¶meters).unwrap(); 468 | 469 | let offspring = genome_0.cross_in(&genome_1, &mut context); 470 | 471 | assert_eq!(offspring.hidden.len(), 1); 472 | assert_eq!(offspring.feed_forward.len(), 5); 473 | } 474 | 475 | #[test] 476 | fn crossover_different_fitness_by_equal_fittnes_different_len() { 477 | todo!("move to individual/mod.rs"); 478 | 479 | let mut parameters: Parameters = Default::default(); 480 | parameters.mutation.weights.perturbation_range = 1.0; 481 | parameters.initialization.activations = vec![Activation::Tanh]; 482 | let mut context = Context::new(¶meters); 483 | 484 | parameters.setup.dimension.input = 2; 485 | parameters.setup.dimension.output = 1; 486 | parameters.initialization.connections = 1.0; 487 | 488 | let mut genome_0 = Genome::new(&mut context, ¶meters); 489 | 490 | genome_0.init(&mut context, ¶meters); 491 | 492 | let mut genome_1 = genome_0.clone(); 493 | // mutate genome_0 494 | genome_0.add_node(&mut context, ¶meters); 495 | 496 | // mutate genome_1 497 | genome_1.add_node(&mut context, ¶meters); 498 | genome_1.add_connection(&mut context, ¶meters).unwrap(); 499 | 500 | let offspring = genome_0.cross_in(&genome_1, &mut context); 501 | 502 | assert_eq!(offspring.hidden.len(), 1); 503 | assert_eq!(offspring.feed_forward.len(), 4); 504 | } 505 | 506 | #[test] 507 | fn detect_no_cycle() { 508 | let mut parameters: Parameters = Default::default(); 509 | parameters.mutation.weights.perturbation_range = 1.0; 510 | let mut context = Context::new(¶meters); 511 | 512 | parameters.setup.dimension.input = 1; 513 | parameters.setup.dimension.output = 1; 514 | parameters.initialization.connections = 1.0; 515 | 516 | let mut genome_0 = Genome::new(&mut context, ¶meters); 517 | 518 | genome_0.init(&mut context, ¶meters); 519 | 520 | let input = genome_0.inputs.iter().next().unwrap(); 521 | let output = genome_0.outputs.iter().next().unwrap(); 522 | 523 | let result = genome_0.would_form_cycle(&input, &output); 524 | 525 | assert!(!result); 526 | } 527 | 528 | #[test] 529 | fn detect_cycle() { 530 | let mut parameters: Parameters = Default::default(); 531 | parameters.mutation.weights.perturbation_range = 1.0; 532 | parameters.initialization.activations = vec![Activation::Tanh]; 533 | let mut context = Context::new(¶meters); 534 | 535 | parameters.setup.dimension.input = 1; 536 | parameters.setup.dimension.output = 1; 537 | parameters.initialization.connections = 1.0; 538 | 539 | let mut genome_0 = Genome::new(&mut context, ¶meters); 540 | 541 | genome_0.init(&mut context, ¶meters); 542 | 543 | // mutate genome_0 544 | genome_0.add_node(&mut context, ¶meters); 545 | 546 | let input = genome_0.inputs.iter().next().unwrap(); 547 | let output = genome_0.outputs.iter().next().unwrap(); 548 | 549 | let result = genome_0.would_form_cycle(&output, &input); 550 | 551 | println!("{:?}", genome_0); 552 | 553 | assert!(result); 554 | } 555 | 556 | #[test] 557 | fn crossover_no_cycle() { 558 | let mut parameters: Parameters = Default::default(); 559 | parameters.mutation.weights.perturbation_std_dev = 1.0; 560 | let mut context = Context::new(¶meters); 561 | 562 | let mut rng = NeatRng::new( 563 | parameters.seed, 564 | parameters.mutation.weights.perturbation_std_dev, 565 | ); 566 | 567 | // assumption: 568 | // crossover of equal fitness genomes should not produce cycles 569 | // prerequisits: 570 | // genomes with equal fitness (0.0 in this case) 571 | // "mirrored" structure as simplest example 572 | 573 | let mut genome_0 = Genome { 574 | inputs: Genes( 575 | vec![Input(Node(Id(0), Activation::Linear))] 576 | .iter() 577 | .cloned() 578 | .collect(), 579 | ), 580 | outputs: Genes( 581 | vec![Output(Node(Id(1), Activation::Linear))] 582 | .iter() 583 | .cloned() 584 | .collect(), 585 | ), 586 | hidden: Genes( 587 | vec![ 588 | Hidden(Node(Id(2), Activation::Tanh)), 589 | Hidden(Node(Id(3), Activation::Tanh)), 590 | ] 591 | .iter() 592 | .cloned() 593 | .collect(), 594 | ), 595 | feed_forward: Genes( 596 | vec![ 597 | FeedForward(Connection(Id(0), Weight::default(), Id(2))), 598 | FeedForward(Connection(Id(2), Weight::default(), Id(1))), 599 | FeedForward(Connection(Id(0), Weight::default(), Id(3))), 600 | FeedForward(Connection(Id(3), Weight::default(), Id(1))), 601 | ] 602 | .iter() 603 | .cloned() 604 | .collect(), 605 | ), 606 | ..Default::default() 607 | }; 608 | 609 | let mut genome_1 = genome_0.clone(); 610 | 611 | // insert connectio one way in genome0 612 | genome_0 613 | .feed_forward 614 | .insert(FeedForward(Connection(Id(2), Weight::default(), Id(3)))); 615 | 616 | // insert connection the other way in genome1 617 | genome_1 618 | .feed_forward 619 | .insert(FeedForward(Connection(Id(3), Weight::default(), Id(2)))); 620 | 621 | let offspring = genome_0.cross_in(&genome_1, &mut rng.small); 622 | 623 | println!("offspring {:?}", offspring); 624 | 625 | for connection0 in offspring.feed_forward.iter() { 626 | for connection1 in offspring.feed_forward.iter() { 627 | println!( 628 | "{:?}->{:?}, {:?}->{:?}", 629 | connection0.input(), 630 | connection0.output(), 631 | connection1.input(), 632 | connection1.output() 633 | ); 634 | assert!( 635 | !(connection0.input() == connection1.output() 636 | && connection0.output() == connection1.input()) 637 | ) 638 | } 639 | } 640 | } 641 | 642 | /* #[test] 643 | fn compatability_distance_same_genome() { 644 | let genome_0 = Genome { 645 | inputs: Genes( 646 | vec![Input(Node(Id(0), Activation::Linear))] 647 | .iter() 648 | .cloned() 649 | .collect(), 650 | ), 651 | outputs: Genes( 652 | vec![Output(Node(Id(1), Activation::Linear))] 653 | .iter() 654 | .cloned() 655 | .collect(), 656 | ), 657 | 658 | feed_forward: Genes( 659 | vec![FeedForward(Connection(Id(0), Weight(1.0), Id(1)))] 660 | .iter() 661 | .cloned() 662 | .collect(), 663 | ), 664 | ..Default::default() 665 | }; 666 | 667 | let genome_1 = genome_0.clone(); 668 | 669 | let delta = Genome::compatability_distance(&genome_0, &genome_1, 1.0, 0.4, 0.0); 670 | 671 | assert!(delta < f64::EPSILON); 672 | } 673 | 674 | #[test] 675 | fn compatability_distance_different_weight_genome() { 676 | let genome_0 = Genome { 677 | inputs: Genes( 678 | vec![Input(Node(Id(0), Activation::Linear))] 679 | .iter() 680 | .cloned() 681 | .collect(), 682 | ), 683 | outputs: Genes( 684 | vec![Output(Node(Id(1), Activation::Linear))] 685 | .iter() 686 | .cloned() 687 | .collect(), 688 | ), 689 | 690 | feed_forward: Genes( 691 | vec![FeedForward(Connection(Id(0), Weight(1.0), Id(1)))] 692 | .iter() 693 | .cloned() 694 | .collect(), 695 | ), 696 | ..Default::default() 697 | }; 698 | 699 | let mut genome_1 = genome_0.clone(); 700 | 701 | genome_1 702 | .feed_forward 703 | .replace(FeedForward(Connection(Id(0), Weight(2.0), Id(1)))); 704 | 705 | println!("genome_0: {:?}", genome_0); 706 | println!("genome_1: {:?}", genome_1); 707 | 708 | let delta = Genome::compatability_distance(&genome_0, &genome_1, 0.0, 2.0, 0.0); 709 | 710 | assert!((delta - 2.0).abs() < f64::EPSILON); 711 | } 712 | 713 | #[test] 714 | fn compatability_distance_different_connection_genome() { 715 | let genome_0 = Genome { 716 | inputs: Genes( 717 | vec![Input(Node(Id(0), Activation::Linear))] 718 | .iter() 719 | .cloned() 720 | .collect(), 721 | ), 722 | outputs: Genes( 723 | vec![Output(Node(Id(1), Activation::Linear))] 724 | .iter() 725 | .cloned() 726 | .collect(), 727 | ), 728 | 729 | feed_forward: Genes( 730 | vec![FeedForward(Connection(Id(0), Weight(1.0), Id(1)))] 731 | .iter() 732 | .cloned() 733 | .collect(), 734 | ), 735 | ..Default::default() 736 | }; 737 | 738 | let mut genome_1 = genome_0.clone(); 739 | 740 | genome_1 741 | .feed_forward 742 | .replace(FeedForward(Connection(Id(0), Weight(1.0), Id(2)))); 743 | genome_1 744 | .feed_forward 745 | .replace(FeedForward(Connection(Id(2), Weight(2.0), Id(1)))); 746 | 747 | println!("genome_0: {:?}", genome_0); 748 | println!("genome_1: {:?}", genome_1); 749 | 750 | let delta = Genome::compatability_distance(&genome_0, &genome_1, 2.0, 0.0, 0.0); 751 | 752 | // factor 2 times 2 different genes 753 | assert!((delta - 2.0 * 2.0).abs() < f64::EPSILON); 754 | } */ */ 755 | } 756 | --------------------------------------------------------------------------------