From 80d3f68b3db833a315c7c6d528f2037b1eaef94d Mon Sep 17 00:00:00 2001 From: Tristan Murphy <72839119+inflectrix@users.noreply.github.com> Date: Fri, 23 Feb 2024 16:46:01 +0000 Subject: [PATCH 01/12] change version --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index e08a721..58f4808 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "neat" description = "Crate for working with NEAT in rust" -version = "0.2.1" +version = "0.3.0" edition = "2021" authors = ["Inflectrix"] repository = "https://github.com/inflectrix/neat" From fd6b84b3e497c028672a2117bfd613e448a3d882 Mon Sep 17 00:00:00 2001 From: Tristan Murphy <72839119+inflectrix@users.noreply.github.com> Date: Fri, 23 Feb 2024 12:35:48 -0500 Subject: [PATCH 02/12] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 140a66c..67ad30a 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Implementation of the NEAT algorithm using `genetic-rs` ### Features - rayon - Uses parallelization on the `NeuralNetwork` struct and adds the `rayon` feature to the `genetic-rs` re-export. - serde - Adds the NNTSerde struct and allows for serialization of `NeuralNetworkTopology` -- crossover - Implements the `CrossoverReproduction` trait on `NeuralNetworkTopology` +- crossover - Implements the `CrossoverReproduction` trait on `NeuralNetworkTopology` and adds the `crossover` feature to the `genetic-rs re-export. ### How To Use When working with this crate, you'll want to use the `NeuralNetworkTopology` struct in your agent's DNA and @@ -83,4 +83,4 @@ fn main() { ``` ### License -This crate falls under the `MIT` license \ No newline at end of file +This crate falls under the `MIT` license From ca76444db639e51978342a564e7ca038cc608bea Mon Sep 17 00:00:00 2001 From: Tristan Murphy <72839119+inflectrix@users.noreply.github.com> Date: Tue, 27 Feb 2024 15:42:03 +0000 Subject: [PATCH 03/12] change to use genetic-rs derive --- Cargo.lock | 32 +++++++++++++++++++++++++++----- Cargo.toml | 2 +- README.md | 18 +----------------- examples/basic.rs | 26 ++------------------------ 4 files changed, 31 insertions(+), 47 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 78fcb3c..e144559 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -50,15 +50,37 @@ checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "genetic-rs" -version = "0.3.0" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "355778de2b2da7f1c4c5db9b98bfa94058159ff732b941b01d552cc0004c8610" +dependencies = [ + "genetic-rs-common", + "genetic-rs-macros", +] + +[[package]] +name = "genetic-rs-common" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "694916cca5538e02a6f04a0fcb5deef9297b460c697520b9f88ea4b9c0a1a09c" +checksum = "4583c0a38736e3ae592a0409d341156c414d0385658810de348ceca2c333575b" dependencies = [ "rand", "rayon", "replace_with", ] +[[package]] +name = "genetic-rs-macros" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dffb2a3f48483f65dd474ffcee8f7d728c6d57c76b9a4279b26febafe5d9027" +dependencies = [ + "genetic-rs-common", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "getrandom" version = "0.2.12" @@ -78,7 +100,7 @@ checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "neat" -version = "0.2.1" +version = "0.3.0" dependencies = [ "bincode", "genetic-rs", @@ -199,9 +221,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.50" +version = "2.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74f1bdc9872430ce9b75da68329d1c1746faf50ffac5f19e02b71e37ff881ffb" +checksum = "6ab617d94515e94ae53b8406c628598680aa0c9587474ecbe58188f7b345d66c" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 58f4808..efb7924 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,7 @@ serde = ["dep:serde", "dep:serde-big-array"] [dependencies] -genetic-rs = "0.3" +genetic-rs = { version = "0.4", features = ["derive"] } rand = "0.8.5" rayon = { version = "1.8.1", optional = true } serde = { version = "1.0.197", features = ["derive"], optional = true } diff --git a/README.md b/README.md index 67ad30a..2502fae 100644 --- a/README.md +++ b/README.md @@ -18,28 +18,12 @@ Here's an example of how one might use this crate: ```rust use neat::*; -#[derive(Clone)] +#[derive(Clone, RandomlyMutable, DivisionReproduction)] struct MyAgentDNA { network: NeuralNetworkTopology<1, 2>, other_stuff: Foo, } -impl RandomlyMutable for MyAgentDNA { - fn mutate(&mut self, rate: f32, rng: &mut impl rand::Rng) { - self.network.mutate(rate, rng); - self.other_stuff.mutate(rate, rng); - } -} - -impl DivisionReproduction for MyAgentDNA { - fn spawn_child(&self, rng: &mut impl rand::Rng) -> Self { - Self { - network: self.network.spawn_child(rng), - // ... - } - } -} - impl GenerateRandom for MyAgentDNA { fn gen_random(rng: &mut impl rand::Rng) -> Self { Self { diff --git a/examples/basic.rs b/examples/basic.rs index 93f5cf9..32e99e6 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -3,36 +3,14 @@ use neat::*; use rand::prelude::*; -#[derive(PartialEq, Clone, Debug)] +#[derive(PartialEq, Clone, Debug, DivisionReproduction, RandomlyMutable)] +#[cfg_attr(feature = "crossover", derive(CrossoverReproduction))] struct AgentDNA { network: NeuralNetworkTopology<2, 4>, } -impl RandomlyMutable for AgentDNA { - fn mutate(&mut self, rate: f32, rng: &mut impl Rng) { - self.network.mutate(rate, rng); - } -} - impl Prunable for AgentDNA {} -impl DivisionReproduction for AgentDNA { - fn divide(&self, rng: &mut impl Rng) -> Self { - let mut child = self.clone(); - child.mutate(self.network.mutation_rate, rng); - child - } -} - -#[cfg(feature = "crossover")] -impl CrossoverReproduction for AgentDNA { - fn crossover(&self, other: &Self, rng: &mut impl Rng) -> Self { - Self { - network: self.network.crossover(&other.network, rng), - } - } -} - impl GenerateRandom for AgentDNA { fn gen_random(rng: &mut impl rand::Rng) -> Self { Self { From 9f4fa5341d3bddca88b2891a8ccca4e3c8778826 Mon Sep 17 00:00:00 2001 From: Tristan Murphy <72839119+inflectrix@users.noreply.github.com> Date: Tue, 27 Feb 2024 17:02:52 +0000 Subject: [PATCH 04/12] change version --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e144559..9980c49 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -100,7 +100,7 @@ checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "neat" -version = "0.3.0" +version = "0.4.0" dependencies = [ "bincode", "genetic-rs", diff --git a/Cargo.toml b/Cargo.toml index efb7924..fa5f9bc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "neat" description = "Crate for working with NEAT in rust" -version = "0.3.0" +version = "0.4.0" edition = "2021" authors = ["Inflectrix"] repository = "https://github.com/inflectrix/neat" From a8f64386d12ca86bad3a7ead6c4372946c434488 Mon Sep 17 00:00:00 2001 From: Tristan Murphy <72839119+inflectrix@users.noreply.github.com> Date: Wed, 28 Feb 2024 12:29:06 +0000 Subject: [PATCH 05/12] add star ref --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2502fae..ab1cf94 100644 --- a/README.md +++ b/README.md @@ -3,12 +3,14 @@ [crates.io](https://crates.io/crates/neat) [docs.rs](https://docs.rs/neat) -Implementation of the NEAT algorithm using `genetic-rs` +Implementation of the NEAT algorithm using `genetic-rs`. ### Features - rayon - Uses parallelization on the `NeuralNetwork` struct and adds the `rayon` feature to the `genetic-rs` re-export. - serde - Adds the NNTSerde struct and allows for serialization of `NeuralNetworkTopology` -- crossover - Implements the `CrossoverReproduction` trait on `NeuralNetworkTopology` and adds the `crossover` feature to the `genetic-rs re-export. +- crossover - Implements the `CrossoverReproduction` trait on `NeuralNetworkTopology` and adds the `crossover` feature to the `genetic-rs` re-export. + +*Do you like this repo and want to support it? If so, leave a ⭐* ### How To Use When working with this crate, you'll want to use the `NeuralNetworkTopology` struct in your agent's DNA and From b9df175cdfa57c58ed882509e1a5104a67732388 Mon Sep 17 00:00:00 2001 From: Tristan Murphy <72839119+inflectrix@users.noreply.github.com> Date: Thu, 29 Feb 2024 12:31:27 +0000 Subject: [PATCH 06/12] improve README example --- README.md | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index ab1cf94..255beb0 100644 --- a/README.md +++ b/README.md @@ -23,36 +23,55 @@ use neat::*; #[derive(Clone, RandomlyMutable, DivisionReproduction)] struct MyAgentDNA { network: NeuralNetworkTopology<1, 2>, - other_stuff: Foo, } impl GenerateRandom for MyAgentDNA { fn gen_random(rng: &mut impl rand::Rng) -> Self { Self { network: NeuralNetworkTopology::new(0.01, 3, rng), - other_stuff: Foo::gen_random(rng), } } } struct MyAgent { network: NeuralNetwork<1, 2>, - some_other_state: Bar, + // ... other state } impl From<&MyAgentDNA> for MyAgent { fn from(value: &MyAgentDNA) -> Self { Self { network: NeuralNetwork::from(&value.network), - some_other_state: Bar::default(), } } } fn fitness(dna: &MyAgentDNA) -> f32 { + // agent will simply try to predict whether a number is greater than 0.5 let mut agent = MyAgent::from(dna); + let mut rng = rand::thread_rng(); + let mut fitness = 0; + + // use repeated tests to avoid situational bias and some local maximums, overall providing more accurate score + for _ in 0..10 { + let n = rng.gen::(); + let above = n > 0.5; + + let res = agent.network.predict([n]); + let resi = res.iter().max_index(); + + if resi == 0 ^ above { + // agent did not guess correctly, punish slightly (too much will hinder exploration) + fitness -= 0.5; + + continue; + } - // ... use agent.network.predict() and agent.network.flush() throughout multiple iterations + // agent guessed correctly, they become more fit. + fitness += 3.; + } + + fitness } fn main() { @@ -64,7 +83,13 @@ fn main() { division_pruning_nextgen, ); - // ... simulate generations, etc. + // simulate 100 generations + for _ in 0..100 { + sim.next_generation(); + } + + // display fitness results + } ``` From 1a684cfd79f2772f2c0f6d448c937b39d0df8183 Mon Sep 17 00:00:00 2001 From: Tristan Murphy <72839119+inflectrix@users.noreply.github.com> Date: Thu, 29 Feb 2024 12:33:37 +0000 Subject: [PATCH 07/12] add dsiplay fitness results to readme example --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 255beb0..ad775e2 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,12 @@ fn main() { } // display fitness results + let fits: Vec<_> = sim.entities + .iter() + .map(fitness) + .collect(); + dbg!(&fits, fits.iter().max()); } ``` From 10863cd24e679d50d2f19f43be3e5dfa88fd3bf6 Mon Sep 17 00:00:00 2001 From: Tristan Murphy <72839119+inflectrix@users.noreply.github.com> Date: Thu, 29 Feb 2024 13:00:17 +0000 Subject: [PATCH 08/12] use serde feature in basic example --- Cargo.lock | 24 ++++++++++++++++++++++++ Cargo.toml | 3 ++- examples/basic.rs | 32 +++++++++++++++++++------------- 3 files changed, 45 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9980c49..e77a241 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -92,6 +92,12 @@ dependencies = [ "wasi", ] +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + [[package]] name = "libc" version = "0.2.153" @@ -108,6 +114,7 @@ dependencies = [ "rayon", "serde", "serde-big-array", + "serde_json", ] [[package]] @@ -190,6 +197,12 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3a8614ee435691de62bcffcf4a66d91b3594bf1428a5722e79103249a095690" +[[package]] +name = "ryu" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" + [[package]] name = "serde" version = "1.0.197" @@ -219,6 +232,17 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_json" +version = "1.0.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" +dependencies = [ + "itoa", + "ryu", + "serde", +] + [[package]] name = "syn" version = "2.0.51" diff --git a/Cargo.toml b/Cargo.toml index fa5f9bc..285c041 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,4 +33,5 @@ serde = { version = "1.0.197", features = ["derive"], optional = true } serde-big-array = { version = "0.5.1", optional = true } [dev-dependencies] -bincode = "1.3.3" \ No newline at end of file +bincode = "1.3.3" +serde_json = "1.0.114" \ No newline at end of file diff --git a/examples/basic.rs b/examples/basic.rs index 32e99e6..66bc686 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -143,14 +143,17 @@ fn main() { sim.next_generation(); } - let fits: Vec<_> = sim.genomes.iter().map(fitness).collect(); + let mut fits: Vec<_> = sim.genomes.iter().map(|e| (e, fitness(e))).collect(); - let maxfit = fits - .iter() - .max_by(|a, b| a.partial_cmp(b).unwrap()) - .unwrap(); + fits.sort_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap()); - dbg!(&fits, maxfit); + dbg!(&fits); + + if cfg!(feature = "serde") { + let intermediate = NNTSerde::from(&fits[0].0.network); + let serialized = serde_json::to_string(&intermediate).unwrap(); + println!("{}", serialized); + } } #[cfg(all(feature = "crossover", feature = "rayon"))] @@ -161,12 +164,15 @@ fn main() { sim.next_generation(); } - let fits: Vec<_> = sim.genomes.iter().map(fitness).collect(); + let mut fits: Vec<_> = sim.genomes.iter().map(|e| (e, fitness(e))).collect(); - let maxfit = fits - .iter() - .max_by(|a, b| a.partial_cmp(b).unwrap()) - .unwrap(); + fits.sort_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap()); - dbg!(&fits, maxfit); -} + dbg!(&fits); + + if cfg!(feature = "serde") { + let intermediate = NNTSerde::from(&fits[0].0.network); + let serialized = serde_json::to_string(&intermediate).unwrap(); + println!("serialized: {}", serialized); + } +} \ No newline at end of file From 344630b0e111cc6c695fb552183249d4014fe9e3 Mon Sep 17 00:00:00 2001 From: Tristan Murphy <72839119+inflectrix@users.noreply.github.com> Date: Thu, 29 Feb 2024 13:01:02 +0000 Subject: [PATCH 09/12] cargo fmt --- examples/basic.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/basic.rs b/examples/basic.rs index 66bc686..bcd5d6d 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -175,4 +175,4 @@ fn main() { let serialized = serde_json::to_string(&intermediate).unwrap(); println!("serialized: {}", serialized); } -} \ No newline at end of file +} From 4f4acf07d24afbac609b16f5630ecadc4b16c010 Mon Sep 17 00:00:00 2001 From: Tristan Murphy <72839119+inflectrix@users.noreply.github.com> Date: Thu, 29 Feb 2024 13:14:07 +0000 Subject: [PATCH 10/12] add a small contribution guide --- CONTRIBUTING | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 CONTRIBUTING diff --git a/CONTRIBUTING b/CONTRIBUTING new file mode 100644 index 0000000..717ca53 --- /dev/null +++ b/CONTRIBUTING @@ -0,0 +1,6 @@ +Thanks for contributing to this project. + +To get started, check out the [issues page](https://github.com.inflectrix/neat). You can either find a feature/fix from there or start a new issue, then begin implementing it in your own fork of this repo. + +Once you are done making the changes you'd like the make, start a pull request to the [dev](https://github.com/inflectrix/neat/tree/dev) branch. State your changes and request a review. After all branch rules have been satisfied, someone with management permissions on this repository will merge it. + From cab98f64baa2bb47d125a9755ea0437b6e49ab98 Mon Sep 17 00:00:00 2001 From: Tristan Murphy <72839119+inflectrix@users.noreply.github.com> Date: Mon, 15 Apr 2024 11:31:10 +0000 Subject: [PATCH 11/12] fix contribution page issues link --- CONTRIBUTING | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING b/CONTRIBUTING index 717ca53..68c1a8c 100644 --- a/CONTRIBUTING +++ b/CONTRIBUTING @@ -1,6 +1,6 @@ Thanks for contributing to this project. -To get started, check out the [issues page](https://github.com.inflectrix/neat). You can either find a feature/fix from there or start a new issue, then begin implementing it in your own fork of this repo. +To get started, check out the [issues page](https://github.com/inflectrix/neat). You can either find a feature/fix from there or start a new issue, then begin implementing it in your own fork of this repo. Once you are done making the changes you'd like the make, start a pull request to the [dev](https://github.com/inflectrix/neat/tree/dev) branch. State your changes and request a review. After all branch rules have been satisfied, someone with management permissions on this repository will merge it. From 8368c1217b30f2be70c671d9c783bd9054902d57 Mon Sep 17 00:00:00 2001 From: Tristan Murphy <72839119+inflectrix@users.noreply.github.com> Date: Mon, 15 Apr 2024 11:43:18 +0000 Subject: [PATCH 12/12] bump genetic-rs from 0.4.0 to 0.5.1 --- Cargo.lock | 12 ++++++------ Cargo.toml | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e77a241..4356c96 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -50,9 +50,9 @@ checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "genetic-rs" -version = "0.4.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "355778de2b2da7f1c4c5db9b98bfa94058159ff732b941b01d552cc0004c8610" +checksum = "b94601f3db2fb341f71a4470134eb1f71d39f54c2fe264122698eda67cd1c91b" dependencies = [ "genetic-rs-common", "genetic-rs-macros", @@ -60,9 +60,9 @@ dependencies = [ [[package]] name = "genetic-rs-common" -version = "0.4.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4583c0a38736e3ae592a0409d341156c414d0385658810de348ceca2c333575b" +checksum = "4f41b0e3f6ccb66a00e7fc9170d4e02b1ae80c85f03c67b76b067b3637fd314a" dependencies = [ "rand", "rayon", @@ -71,9 +71,9 @@ dependencies = [ [[package]] name = "genetic-rs-macros" -version = "0.4.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dffb2a3f48483f65dd474ffcee8f7d728c6d57c76b9a4279b26febafe5d9027" +checksum = "2d5ec3b9e69a6836bb0f0c8fa6972e6322e0b49108f7b3ed40769feb452c120a" dependencies = [ "genetic-rs-common", "proc-macro2", diff --git a/Cargo.toml b/Cargo.toml index 285c041..acc8fcc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,7 @@ serde = ["dep:serde", "dep:serde-big-array"] [dependencies] -genetic-rs = { version = "0.4", features = ["derive"] } +genetic-rs = { version = "0.5.1", features = ["derive"] } rand = "0.8.5" rayon = { version = "1.8.1", optional = true } serde = { version = "1.0.197", features = ["derive"], optional = true }