diff --git a/src/bin/bna.rs b/src/bin/bna.rs index 0758eec..f3937ec 100644 --- a/src/bin/bna.rs +++ b/src/bin/bna.rs @@ -1,12 +1,10 @@ -use std::f64::consts::PI; use neuroevolution::vneuron::*; +use neuroevolution::bna::*; fn main() { - let input = vec![1., PI / 8.]; - let dim = 2; - let bend = PI / 4.; - let angles = vec![PI / 4.]; - let bias = 0.; - let vneuron = VNeuron::from_params(dim, bias, angles, bend); - println!("{:?}", vneuron.evaluate(&input)); + let mut vneuron = VNeuron::new(2); + + vneuron.optimize(half, 1000); + println!("half fitness: {}", half(&vneuron)); + println!("Half: {}", vneuron); } diff --git a/src/bin/oneplusone_na.rs b/src/bin/oneplusone_na.rs index b30e0ac..7af0577 100644 --- a/src/bin/oneplusone_na.rs +++ b/src/bin/oneplusone_na.rs @@ -1,4 +1,4 @@ -use neuroevolution::one_plus_one_na::*; +use neuroevolution::oneplusone_na::*; use neuroevolution::network::Network; fn main() { diff --git a/src/bna.rs b/src/bna.rs new file mode 100644 index 0000000..803eec8 --- /dev/null +++ b/src/bna.rs @@ -0,0 +1,16 @@ +use std::f64::consts::PI; +use crate::vneuron::*; + +const UNIT_CIRCLE_STEPS: u32 = 1000; + +pub fn half(vneuron: &VNeuron) -> f64 { + let mut sum = 0; + for i in 0..UNIT_CIRCLE_STEPS { + let angle = 2. * PI * i as f64 / UNIT_CIRCLE_STEPS as f64; + let output = vneuron.evaluate(&vec![1., angle]); + if output && angle < PI || !output && angle > PI { + sum += 1; + } + } + sum as f64 / UNIT_CIRCLE_STEPS as f64 +} diff --git a/src/cma_es.rs b/src/cma_es.rs index 95209df..2985a3a 100644 --- a/src/cma_es.rs +++ b/src/cma_es.rs @@ -3,25 +3,25 @@ use crate::network::*; pub fn half(x: &DVector) -> f64 { let network: Network = get_network(&x); - crate::one_plus_one_na::half(&network) + crate::oneplusone_na::half(&network) } pub fn quarter(x: &DVector) -> f64 { let network: Network = get_network(&x); - crate::one_plus_one_na::quarter(&network) + crate::oneplusone_na::quarter(&network) } pub fn two_quarters(x: &DVector) -> f64 { let network: Network = get_network(&x); - crate::one_plus_one_na::two_quarters(&network) + crate::oneplusone_na::two_quarters(&network) } pub fn square(x: &DVector) -> f64 { let network: Network = get_network(&x); - crate::one_plus_one_na::square(&network) + crate::oneplusone_na::square(&network) } pub fn cube(x: &DVector) -> f64 { let network: Network = get_network(&x); - crate::one_plus_one_na::cube(&network) + crate::oneplusone_na::cube(&network) } diff --git a/src/lib.rs b/src/lib.rs index f66b0d4..7844f9f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ -pub mod one_plus_one_na; +pub mod oneplusone_na; pub mod cma_es; pub mod network; pub mod vneuron; pub mod utils; +pub mod bna; diff --git a/src/one_plus_one_na.rs b/src/oneplusone_na.rs similarity index 100% rename from src/one_plus_one_na.rs rename to src/oneplusone_na.rs diff --git a/src/vneuron.rs b/src/vneuron.rs index 9e0c1e2..2f23467 100644 --- a/src/vneuron.rs +++ b/src/vneuron.rs @@ -1,5 +1,7 @@ +use std::fmt; use std::f64::consts::PI; use rand::prelude::*; +use rand_distr::{Exp, Distribution}; use crate::utils::*; #[derive(Debug, Clone)] @@ -10,13 +12,32 @@ pub struct VNeuron { pub bend: f64, } +impl fmt::Display for VNeuron { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut formatted_angles = String::new(); + for angle in &self.angles { + let formatted_angle = format!("{:.2}", angle * 2.0 * PI); + formatted_angles.push_str(&formatted_angle); + formatted_angles.push_str(", "); + } + + let formatted_bias = format!("{:.2}", self.bias * 2.0 - 1.0); + let formatted_bend = format!("{:.2}", self.bend * PI); + + write!( + f, + "VNeuron {{ dim: {}, bias: {}, angles: [{}], bend: {} }}", + self.dim, formatted_bias, formatted_angles, formatted_bend + ) + } +} + impl VNeuron { pub fn new(dim: usize) -> Self { - // genereate random values between 0 and 1 Self { dim, bias: random(), - angles: vec![random(); dim], + angles: vec![random(); dim - 1], bend: random(), } } @@ -26,7 +47,35 @@ impl VNeuron { dim, bias: (bias + 1.) / 2., angles: angles.iter().map(|x| x / (2. * PI)).collect(), - bend: bend / (2. * PI), + bend: bend / PI, + } + } + + fn mutate_component(component: &mut f64) { + let exp = Exp::new(1.).unwrap(); + let sign = if random::() < 0.5 { 1. } else { -1. }; + *component += sign * exp.sample(&mut thread_rng()); + *component -= component.floor(); + } + + pub fn optimize(&mut self, evaluation_function: fn(&VNeuron) -> f64, n_iters: u32) { + for _ in 0..n_iters { + let mut new_vneuron = self.clone(); + if random::() < 1. / 3. { + VNeuron::mutate_component(&mut new_vneuron.bend); + } + for i in 0..self.dim-1 { + if random::() < 1. / 3. { + VNeuron::mutate_component(&mut new_vneuron.angles[i]); + } + } + if random::() < 1. / 3. { + VNeuron::mutate_component(&mut new_vneuron.bias); + } + + if evaluation_function(&new_vneuron) > evaluation_function(self) { + *self = new_vneuron; + } } }