From 4f74e87189ddbffb4e441165b7f048ec92822d5c Mon Sep 17 00:00:00 2001 From: Tristan Murphy <72839119+inflectrix@users.noreply.github.com> Date: Wed, 14 Feb 2024 12:44:37 +0000 Subject: [PATCH 01/11] fix addneuron and add removeneuron --- src/topology.rs | 46 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/src/topology.rs b/src/topology.rs index 2c9f09f..a296c86 100644 --- a/src/topology.rs +++ b/src/topology.rs @@ -163,8 +163,12 @@ impl RandomlyMutable for NeuralNetworkTopology RandomlyMutable for NeuralNetworkTopology() <= rate && !self.hidden_layers.is_empty() { + // remove a neuron + let (_, mut loc) = self.rand_neuron(rng); + + while !loc.is_hidden() { + (_, loc) = self.rand_neuron(rng); + } + + self.hidden_layers.remove(loc.unwrap()); + + let mut done = false; + 'outer: for n in &self.hidden_layers { + let n2 = n.write().unwrap(); + + for (i, (loc2, _)) in n2.inputs.iter().enumerate() { + if i == loc { + n2.inputs.remove(i); + done = true; + break 'outer; + } + } + } + + if !done { + 'outer: for n in &self.output_layer { + let n2 = n.write().unwrap(); + + for (i, (loc2, _)) in n2.inputs.iter().enumerate() { + if i == loc { + n2.inputs.remove(i); + done = true; + break 'outer; + } + } + } + } + } + if rng.gen::() <= rate { // mutate a connection let (mut n, _) = self.rand_neuron(rng); @@ -211,12 +253,14 @@ impl DivisionReproduction for NeuralNetworkTopol } } +/* #[cfg(feature = "crossover")] impl CrossoverReproduction for NeuralNetworkTopology { fn crossover(&self, other: &Self, rng: &mut impl Rng) -> Self { todo!(); } } +*/ /// A stateless version of [`Neuron`][crate::Neuron]. #[derive(Debug, Clone)] From e255eaf4f472206b9e9d1a1f06185433a7bbfc52 Mon Sep 17 00:00:00 2001 From: Tristan Murphy <72839119+inflectrix@users.noreply.github.com> Date: Wed, 14 Feb 2024 14:17:20 +0000 Subject: [PATCH 02/11] add bias mutation --- src/topology.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/topology.rs b/src/topology.rs index a296c86..e8111fd 100644 --- a/src/topology.rs +++ b/src/topology.rs @@ -241,6 +241,14 @@ impl RandomlyMutable for NeuralNetworkTopology() <= rate { + // mutate bias + let (n, _) = self.rand_neuron(rng); + let mut n = n.write().unwrap(); + + n.bias += rng.gen_range(-1.0..1.0) * rate; + } } } } From c9efe0e31ba66a6ac9df28a079b0a0fc05b6f52d Mon Sep 17 00:00:00 2001 From: Tristan Murphy <72839119+inflectrix@users.noreply.github.com> Date: Wed, 14 Feb 2024 16:46:14 +0000 Subject: [PATCH 03/11] implement activation function mutation --- Cargo.toml | 2 +- src/runnable.rs | 12 ++++-- src/topology.rs | 101 +++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 101 insertions(+), 14 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 590afdc..70b272b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ license = "MIT" [features] default = ["max-index"] -#crossover = ["genetic-rs/crossover"] +crossover = ["genetic-rs/crossover"] rayon = ["genetic-rs/rayon", "dep:rayon"] max-index = [] diff --git a/src/runnable.rs b/src/runnable.rs index efcd3b2..360b74a 100644 --- a/src/runnable.rs +++ b/src/runnable.rs @@ -82,7 +82,7 @@ impl NeuralNetwork { n.state.value += self.process_neuron(l) * w; } - n.sigmoid(); + n.activate(); n.state.value } @@ -240,6 +240,9 @@ pub struct Neuron { /// The current state of the neuron. pub state: NeuronState, + + /// The neuron's activation function + pub activation: ActivationFn, } impl Neuron { @@ -248,9 +251,9 @@ impl Neuron { self.state.value = self.bias; } - /// Applies the sigoid activation function to the state's current value. - pub fn sigmoid(&mut self) { - self.state.value = 1. / (1. + std::f32::consts::E.powf(-self.state.value)) + /// Applies the activation function to the neuron + pub fn activate(&mut self) { + self.state.value = (self.activation.func)(self.state.value); } } @@ -263,6 +266,7 @@ impl From<&NeuronTopology> for Neuron { value: value.bias, ..Default::default() }, + activation: value.activation.clone(), } } } diff --git a/src/topology.rs b/src/topology.rs index e8111fd..b264613 100644 --- a/src/topology.rs +++ b/src/topology.rs @@ -1,8 +1,23 @@ -use std::sync::{Arc, RwLock}; +use std::{fmt, sync::{Arc, RwLock}}; use genetic_rs::prelude::*; use rand::prelude::*; +/// Creates an [`ActivationFn`] object from a function +#[macro_export] +macro_rules! activation_fn { + ($F: path) => { + ActivationFn { + func: Arc::new($F), + name: String::from(stringify!($F)), + } + }; + + {$($F: path),*} => { + [$(activation_fn!($F)),*] + }; +} + /// A stateless neural network topology. /// This is the struct you want to use in your agent's inheritance. /// See [`NeuralNetwork::from`][crate::NeuralNetwork::from] for how to convert this to a runnable neural network. @@ -28,7 +43,7 @@ impl NeuralNetworkTopology { /// Creates a new [`NeuralNetworkTopology`]. pub fn new(mutation_rate: f32, mutation_passes: usize, rng: &mut impl Rng) -> Self { let input_layer: [Arc>; I] = (0..I) - .map(|_| Arc::new(RwLock::new(NeuronTopology::new(vec![], rng)))) + .map(|_| Arc::new(RwLock::new(NeuronTopology::new_with_activation(vec![], activation_fn!(linear_activation), rng)))) .collect::>() .try_into() .unwrap(); @@ -51,7 +66,7 @@ impl NeuralNetworkTopology { }) .collect(); - output_layer.push(Arc::new(RwLock::new(NeuronTopology::new(input, rng)))); + output_layer.push(Arc::new(RwLock::new(NeuronTopology::new_with_activation(input, activation_fn!(sigmoid), rng)))); } let output_layer = output_layer.try_into().unwrap(); @@ -202,10 +217,10 @@ impl RandomlyMutable for NeuralNetworkTopology RandomlyMutable for NeuralNetworkTopology RandomlyMutable for NeuralNetworkTopology() <= rate && !self.hidden_layers.is_empty() { + // mutate activation function + let activations = activation_fn! { + sigmoid, + relu, + f32::tanh + }; + + let (mut n, mut loc) = self.rand_neuron(rng); + + while !loc.is_hidden() { + (n, loc) = self.rand_neuron(rng); + } + + let mut nw = n.write().unwrap(); + + // should probably not clone, but its not a huge efficiency issue anyways + nw.activation = activations[rng.gen_range(0..activations.len())].clone(); + } } } } @@ -270,6 +304,33 @@ impl CrossoverReproduction for NeuralNetworkTopology { } */ +/// An activation function object that implements [`fmt::Debug`] and is [`Send`] +#[derive(Clone)] +pub struct ActivationFn { + /// The actual activation function. + pub func: Arc f32 + Send + 'static>, + name: String, +} + +impl fmt::Debug for ActivationFn { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + writeln!(f, "{}", self.name) + } +} + +/// The sigmoid activation function. +pub fn sigmoid(n: f32) -> f32 { + 1. / (1. + std::f32::consts::E.powf(-n)) +} + +/// The ReLU activation function. +pub fn relu(n: f32) -> f32 { + n.max(0.) +} + +/// Activation function that does nothing. +pub fn linear_activation(n: f32) -> f32 {n} + /// A stateless version of [`Neuron`][crate::Neuron]. #[derive(Debug, Clone)] pub struct NeuronTopology { @@ -278,16 +339,38 @@ pub struct NeuronTopology { /// The neuron's bias. pub bias: f32, + + /// The neuron's activation function. + pub activation: ActivationFn, } impl NeuronTopology { /// Creates a new neuron with the given input locations. pub fn new(inputs: Vec, rng: &mut impl Rng) -> Self { - let inputs = inputs.into_iter().map(|i| (i, rng.gen::())).collect(); + let activations = activation_fn! { + sigmoid, + relu, + f32::tanh + }; + + Self::new_with_activations(inputs, activations, rng) + } + + /// Takes a collection of activation functions and chooses a random one to use. + pub fn new_with_activations(inputs: Vec, activations: impl IntoIterator, rng: &mut impl Rng) -> Self { + let mut activations: Vec<_> = activations.into_iter().collect(); + + Self::new_with_activation(inputs, activations.remove(rng.gen_range(0..activations.len())), rng) + } + + /// Creates a neuron with the activation. + pub fn new_with_activation(inputs: Vec, activation: ActivationFn, rng: &mut impl Rng) -> Self { + let inputs = inputs.into_iter().map(|i| (i, rng.gen_range(-1.0..1.0))).collect(); Self { inputs, bias: rng.gen(), + activation, } } } From 174313d84eb18188056a3e1ffa9e225cc2b6f3aa Mon Sep 17 00:00:00 2001 From: Tristan Murphy <72839119+inflectrix@users.noreply.github.com> Date: Wed, 14 Feb 2024 16:55:18 +0000 Subject: [PATCH 04/11] fix making ActivationFn sync --- src/runnable.rs | 2 +- src/topology.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/runnable.rs b/src/runnable.rs index 360b74a..5bdb82d 100644 --- a/src/runnable.rs +++ b/src/runnable.rs @@ -112,7 +112,7 @@ impl NeuralNetwork { let mut nw = n.write().unwrap(); nw.state.value += val; - nw.sigmoid(); + nw.activate(); nw.state.value } diff --git a/src/topology.rs b/src/topology.rs index b264613..f369ddf 100644 --- a/src/topology.rs +++ b/src/topology.rs @@ -308,7 +308,7 @@ impl CrossoverReproduction for NeuralNetworkTopology { #[derive(Clone)] pub struct ActivationFn { /// The actual activation function. - pub func: Arc f32 + Send + 'static>, + pub func: Arc f32 + Send + Sync + 'static>, name: String, } From 582edc0b9684c97394195c818d10c7cb564793b7 Mon Sep 17 00:00:00 2001 From: Tristan Murphy <72839119+inflectrix@users.noreply.github.com> Date: Wed, 14 Feb 2024 16:56:18 +0000 Subject: [PATCH 05/11] cargo fmt --- src/topology.rs | 51 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 40 insertions(+), 11 deletions(-) diff --git a/src/topology.rs b/src/topology.rs index f369ddf..b5ae19a 100644 --- a/src/topology.rs +++ b/src/topology.rs @@ -1,4 +1,7 @@ -use std::{fmt, sync::{Arc, RwLock}}; +use std::{ + fmt, + sync::{Arc, RwLock}, +}; use genetic_rs::prelude::*; use rand::prelude::*; @@ -43,7 +46,13 @@ impl NeuralNetworkTopology { /// Creates a new [`NeuralNetworkTopology`]. pub fn new(mutation_rate: f32, mutation_passes: usize, rng: &mut impl Rng) -> Self { let input_layer: [Arc>; I] = (0..I) - .map(|_| Arc::new(RwLock::new(NeuronTopology::new_with_activation(vec![], activation_fn!(linear_activation), rng)))) + .map(|_| { + Arc::new(RwLock::new(NeuronTopology::new_with_activation( + vec![], + activation_fn!(linear_activation), + rng, + ))) + }) .collect::>() .try_into() .unwrap(); @@ -66,7 +75,11 @@ impl NeuralNetworkTopology { }) .collect(); - output_layer.push(Arc::new(RwLock::new(NeuronTopology::new_with_activation(input, activation_fn!(sigmoid), rng)))); + output_layer.push(Arc::new(RwLock::new(NeuronTopology::new_with_activation( + input, + activation_fn!(sigmoid), + rng, + )))); } let output_layer = output_layer.try_into().unwrap(); @@ -182,8 +195,7 @@ impl RandomlyMutable for NeuralNetworkTopology RandomlyMutable for NeuralNetworkTopology f32 { } /// Activation function that does nothing. -pub fn linear_activation(n: f32) -> f32 {n} +pub fn linear_activation(n: f32) -> f32 { + n +} /// A stateless version of [`Neuron`][crate::Neuron]. #[derive(Debug, Clone)] @@ -357,15 +371,30 @@ impl NeuronTopology { } /// Takes a collection of activation functions and chooses a random one to use. - pub fn new_with_activations(inputs: Vec, activations: impl IntoIterator, rng: &mut impl Rng) -> Self { + pub fn new_with_activations( + inputs: Vec, + activations: impl IntoIterator, + rng: &mut impl Rng, + ) -> Self { let mut activations: Vec<_> = activations.into_iter().collect(); - Self::new_with_activation(inputs, activations.remove(rng.gen_range(0..activations.len())), rng) + Self::new_with_activation( + inputs, + activations.remove(rng.gen_range(0..activations.len())), + rng, + ) } /// Creates a neuron with the activation. - pub fn new_with_activation(inputs: Vec, activation: ActivationFn, rng: &mut impl Rng) -> Self { - let inputs = inputs.into_iter().map(|i| (i, rng.gen_range(-1.0..1.0))).collect(); + pub fn new_with_activation( + inputs: Vec, + activation: ActivationFn, + rng: &mut impl Rng, + ) -> Self { + let inputs = inputs + .into_iter() + .map(|i| (i, rng.gen_range(-1.0..1.0))) + .collect(); Self { inputs, From 1f0bf3bea145271bbc3bf970eee3254e43a8eeed Mon Sep 17 00:00:00 2001 From: Tristan Murphy <72839119+inflectrix@users.noreply.github.com> Date: Wed, 14 Feb 2024 17:54:58 +0000 Subject: [PATCH 06/11] fix topology index error (runnable still an issue) --- src/topology.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/topology.rs b/src/topology.rs index b5ae19a..4f4e7f7 100644 --- a/src/topology.rs +++ b/src/topology.rs @@ -138,6 +138,30 @@ impl NeuralNetworkTopology { } } } + + fn deletion_shift(&self, deleted: NeuronLocation) { + if !deleted.is_hidden() { + panic!("Invalid neuron deletion"); + } + + for n in &self.hidden_layers { + let mut nw = n.write().unwrap(); + for (loc, _w) in &mut nw.inputs { + if loc.is_hidden() && loc.unwrap() > deleted.unwrap() { + *loc = NeuronLocation::Hidden(loc.unwrap() - 1); + } + } + } + + for n in &self.output_layer { + let mut nw = n.write().unwrap(); + for (loc, _w) in &mut nw.inputs { + if loc.is_hidden() && loc.unwrap() > deleted.unwrap() { + *loc = NeuronLocation::Hidden(loc.unwrap() - 1); + } + } + } + } } // need to do all this manually because Arcs are cringe @@ -252,6 +276,8 @@ impl RandomlyMutable for NeuralNetworkTopology() <= rate { From 5fcfffbf920f4c94a7b82b38563eb6fc726b938b Mon Sep 17 00:00:00 2001 From: Tristan Murphy <72839119+inflectrix@users.noreply.github.com> Date: Fri, 16 Feb 2024 15:07:40 +0000 Subject: [PATCH 07/11] restructure neuron deletion (and potentially fix) --- src/topology.rs | 61 +++++++++++++++++-------------------------------- 1 file changed, 21 insertions(+), 40 deletions(-) diff --git a/src/topology.rs b/src/topology.rs index 4f4e7f7..306d751 100644 --- a/src/topology.rs +++ b/src/topology.rs @@ -139,28 +139,36 @@ impl NeuralNetworkTopology { } } - fn deletion_shift(&self, deleted: NeuronLocation) { - if !deleted.is_hidden() { + fn delete_neuron(&mut self, loc: NeuronLocation) -> NeuronTopology { + if !loc.is_hidden() { panic!("Invalid neuron deletion"); } - + + let index = loc.unwrap(); + let n = Arc::into_inner(self.hidden_layers.remove(index)) + .unwrap() + .into_inner() + .unwrap(); + for n in &self.hidden_layers { let mut nw = n.write().unwrap(); for (loc, _w) in &mut nw.inputs { - if loc.is_hidden() && loc.unwrap() > deleted.unwrap() { + if loc.is_hidden() && loc.unwrap() > index { *loc = NeuronLocation::Hidden(loc.unwrap() - 1); } } } - - for n in &self.output_layer { - let mut nw = n.write().unwrap(); - for (loc, _w) in &mut nw.inputs { - if loc.is_hidden() && loc.unwrap() > deleted.unwrap() { - *loc = NeuronLocation::Hidden(loc.unwrap() - 1); + + for n2 in &self.output_layer { + let mut nw = n2.write().unwrap(); + for (iloc, _w) in &mut nw.inputs { + if iloc.is_hidden() && iloc.unwrap() > index { + *iloc = NeuronLocation::Hidden(loc.unwrap() - 1); } } } + + n } } @@ -248,36 +256,9 @@ impl RandomlyMutable for NeuralNetworkTopology() <= rate { From 8ea5fd7f1171664eb2bfd332b87482ac1c244666 Mon Sep 17 00:00:00 2001 From: Tristan Murphy <72839119+inflectrix@users.noreply.github.com> Date: Fri, 16 Feb 2024 16:14:12 +0000 Subject: [PATCH 08/11] fix index error --- src/topology.rs | 50 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/src/topology.rs b/src/topology.rs index 306d751..8e291ff 100644 --- a/src/topology.rs +++ b/src/topology.rs @@ -145,30 +145,46 @@ impl NeuralNetworkTopology { } let index = loc.unwrap(); - let n = Arc::into_inner(self.hidden_layers.remove(index)) - .unwrap() - .into_inner() - .unwrap(); - + let neuron = Arc::into_inner(self.hidden_layers.remove(index)).unwrap(); + for n in &self.hidden_layers { let mut nw = n.write().unwrap(); - for (loc, _w) in &mut nw.inputs { - if loc.is_hidden() && loc.unwrap() > index { - *loc = NeuronLocation::Hidden(loc.unwrap() - 1); - } - } + + nw.inputs = nw.inputs + .iter() + .filter_map(|&(input_loc, w)| { + if !input_loc.is_hidden() { + return Some((input_loc, w)); + } + + if input_loc.unwrap() == index { + return None; + } + + Some((NeuronLocation::Hidden(input_loc.unwrap() - 1), w)) + }) + .collect(); } for n2 in &self.output_layer { let mut nw = n2.write().unwrap(); - for (iloc, _w) in &mut nw.inputs { - if iloc.is_hidden() && iloc.unwrap() > index { - *iloc = NeuronLocation::Hidden(loc.unwrap() - 1); - } - } + nw.inputs = nw.inputs + .iter() + .filter_map(|&(input_loc, w)| { + if !input_loc.is_hidden() { + return Some((input_loc, w)); + } + + if input_loc.unwrap() == index { + return None; + } + + Some((NeuronLocation::Hidden(input_loc.unwrap() - 1), w)) // TODO fix attempt to subtract with overflow (no idea) + }) + .collect(); } - n + neuron.into_inner().unwrap() } } @@ -225,7 +241,7 @@ impl RandomlyMutable for NeuralNetworkTopology Date: Fri, 16 Feb 2024 17:00:36 +0000 Subject: [PATCH 09/11] fix remaining subtraction/index errors --- src/topology.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/topology.rs b/src/topology.rs index 8e291ff..a639cff 100644 --- a/src/topology.rs +++ b/src/topology.rs @@ -124,11 +124,7 @@ impl NeuralNetworkTopology { let i = rng.gen_range(0..self.input_layer.len()); (self.input_layer[i].clone(), NeuronLocation::Input(i)) } - 1 => { - if self.hidden_layers.is_empty() { - return self.rand_neuron(rng); - } - + 1 if !self.hidden_layers.is_empty() => { let i = rng.gen_range(0..self.hidden_layers.len()); (self.hidden_layers[i].clone(), NeuronLocation::Hidden(i)) } @@ -161,7 +157,11 @@ impl NeuralNetworkTopology { return None; } - Some((NeuronLocation::Hidden(input_loc.unwrap() - 1), w)) + if input_loc.unwrap() > index { + return Some((NeuronLocation::Hidden(input_loc.unwrap() - 1), w)); + } + + Some((input_loc, w)) }) .collect(); } @@ -179,7 +179,11 @@ impl NeuralNetworkTopology { return None; } - Some((NeuronLocation::Hidden(input_loc.unwrap() - 1), w)) // TODO fix attempt to subtract with overflow (no idea) + if input_loc.unwrap() > index { + return Some((NeuronLocation::Hidden(input_loc.unwrap() - 1), w)); + } + + Some((input_loc, w)) }) .collect(); } From 8dd5b685d1d197bd018939f02f4d8fdc365dc9ce Mon Sep 17 00:00:00 2001 From: Tristan Murphy <72839119+inflectrix@users.noreply.github.com> Date: Tue, 20 Feb 2024 17:49:36 +0000 Subject: [PATCH 10/11] fix is_connection_cyclic --- src/topology.rs | 64 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 43 insertions(+), 21 deletions(-) diff --git a/src/topology.rs b/src/topology.rs index a639cff..53f25dc 100644 --- a/src/topology.rs +++ b/src/topology.rs @@ -1,6 +1,5 @@ use std::{ - fmt, - sync::{Arc, RwLock}, + collections::HashSet, fmt, sync::{Arc, RwLock} }; use genetic_rs::prelude::*; @@ -93,17 +92,47 @@ impl NeuralNetworkTopology { } } - fn is_connection_cyclic(&self, loc1: NeuronLocation, loc2: NeuronLocation) -> bool { - if loc1 == loc2 { + /// Creates a new connection between the neurons. + /// If the connection is cyclic, it does not add a connection and returns false. + /// Otherwise, it returns true. + pub fn add_connection(&mut self, from: NeuronLocation, to: NeuronLocation, weight: f32) -> bool { + if self.is_connection_cyclic(from, to) { + return false; + } + + // Add the connection since it is not cyclic + self.get_neuron(to).write().unwrap().inputs.push((from, weight)); + + true + } + + fn is_connection_cyclic(&self, from: NeuronLocation, to: NeuronLocation) -> bool { + if to.is_input() || from.is_output() { return true; } - for &(n, _w) in &self.get_neuron(loc1).read().unwrap().inputs { - if self.is_connection_cyclic(n, loc2) { + let mut visited = HashSet::new(); + self.dfs(from, to, &mut visited) + } + + // TODO rayon implementation + fn dfs(&self, current: NeuronLocation, target: NeuronLocation, visited: &mut HashSet) -> bool { + if current == target { + return true; + } + + visited.insert(current); + + let n = self.get_neuron(current); + let nr = n.read().unwrap(); + + for &(input, _) in &nr.inputs { + if !visited.contains(&input) && self.dfs(input, target, visited) { return true; } } - + + visited.remove(¤t); false } @@ -244,8 +273,7 @@ impl RandomlyMutable for NeuralNetworkTopology RandomlyMutable for NeuralNetworkTopology() <= rate { // add a connection - let (mut n1, mut loc1) = self.rand_neuron(rng); + let (_, mut loc1) = self.rand_neuron(rng); + let (_, mut loc2) = self.rand_neuron(rng); - while n1.read().unwrap().inputs.is_empty() { - (n1, loc1) = self.rand_neuron(rng); + while loc1.is_output() || !self.add_connection(loc1, loc2, rng.gen::()) { + (_, loc1) = self.rand_neuron(rng); + (_, loc2) = self.rand_neuron(rng); } - - let (mut n2, mut loc2) = self.rand_neuron(rng); - - while self.is_connection_cyclic(loc1, loc2) { - (n2, loc2) = self.rand_neuron(rng); - } - - n2.write().unwrap().inputs.push((loc1, rng.gen())); } if rng.gen::() <= rate && !self.hidden_layers.is_empty() { @@ -432,7 +454,7 @@ impl NeuronTopology { } /// A pseudo-pointer of sorts used to make structural conversions very fast and easy to write. -#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[derive(Hash, Clone, Copy, Debug, Eq, PartialEq)] pub enum NeuronLocation { /// Points to a neuron in the input layer at contained index. Input(usize), From 7a8396a89063a158794f793a11d291521b78f2e8 Mon Sep 17 00:00:00 2001 From: Tristan Murphy <72839119+inflectrix@users.noreply.github.com> Date: Tue, 20 Feb 2024 17:50:54 +0000 Subject: [PATCH 11/11] cargo fmt --- src/topology.rs | 54 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 36 insertions(+), 18 deletions(-) diff --git a/src/topology.rs b/src/topology.rs index 53f25dc..384de65 100644 --- a/src/topology.rs +++ b/src/topology.rs @@ -1,5 +1,7 @@ use std::{ - collections::HashSet, fmt, sync::{Arc, RwLock} + collections::HashSet, + fmt, + sync::{Arc, RwLock}, }; use genetic_rs::prelude::*; @@ -95,17 +97,26 @@ impl NeuralNetworkTopology { /// Creates a new connection between the neurons. /// If the connection is cyclic, it does not add a connection and returns false. /// Otherwise, it returns true. - pub fn add_connection(&mut self, from: NeuronLocation, to: NeuronLocation, weight: f32) -> bool { + pub fn add_connection( + &mut self, + from: NeuronLocation, + to: NeuronLocation, + weight: f32, + ) -> bool { if self.is_connection_cyclic(from, to) { return false; } - + // Add the connection since it is not cyclic - self.get_neuron(to).write().unwrap().inputs.push((from, weight)); - + self.get_neuron(to) + .write() + .unwrap() + .inputs + .push((from, weight)); + true } - + fn is_connection_cyclic(&self, from: NeuronLocation, to: NeuronLocation) -> bool { if to.is_input() || from.is_output() { return true; @@ -114,24 +125,29 @@ impl NeuralNetworkTopology { let mut visited = HashSet::new(); self.dfs(from, to, &mut visited) } - + // TODO rayon implementation - fn dfs(&self, current: NeuronLocation, target: NeuronLocation, visited: &mut HashSet) -> bool { + fn dfs( + &self, + current: NeuronLocation, + target: NeuronLocation, + visited: &mut HashSet, + ) -> bool { if current == target { return true; } - + visited.insert(current); - + let n = self.get_neuron(current); let nr = n.read().unwrap(); - + for &(input, _) in &nr.inputs { if !visited.contains(&input) && self.dfs(input, target, visited) { return true; } } - + visited.remove(¤t); false } @@ -168,14 +184,15 @@ impl NeuralNetworkTopology { if !loc.is_hidden() { panic!("Invalid neuron deletion"); } - + let index = loc.unwrap(); let neuron = Arc::into_inner(self.hidden_layers.remove(index)).unwrap(); for n in &self.hidden_layers { let mut nw = n.write().unwrap(); - nw.inputs = nw.inputs + nw.inputs = nw + .inputs .iter() .filter_map(|&(input_loc, w)| { if !input_loc.is_hidden() { @@ -194,10 +211,11 @@ impl NeuralNetworkTopology { }) .collect(); } - + for n2 in &self.output_layer { let mut nw = n2.write().unwrap(); - nw.inputs = nw.inputs + nw.inputs = nw + .inputs .iter() .filter_map(|&(input_loc, w)| { if !input_loc.is_hidden() { @@ -216,7 +234,7 @@ impl NeuralNetworkTopology { }) .collect(); } - + neuron.into_inner().unwrap() } } @@ -298,7 +316,7 @@ impl RandomlyMutable for NeuralNetworkTopology