Skip to content

Commit

Permalink
refactored evaluate to output floats
Browse files Browse the repository at this point in the history
  • Loading branch information
samyhaff committed Apr 17, 2024
1 parent 7d60aa7 commit 55fd22c
Show file tree
Hide file tree
Showing 8 changed files with 134 additions and 104 deletions.
51 changes: 24 additions & 27 deletions src/benchmarks.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::f64::consts::PI;
use crate::neuroevolution_algorithm::*;

pub type LabeledPoint = (Vec<f64>, bool);
pub type LabeledPoint = (Vec<f64>, f64);
pub type LabeledPoints = Vec<LabeledPoint>;

#[derive(Debug)]
Expand All @@ -28,17 +28,14 @@ pub trait ClassificationProblemEval {
}
_ => {
let points = self.get_points();
points
let distances_sum = points
.iter()
.map(|(point, label)| {
let output = alg.evaluate(point);
if output && *label || !output && !*label {
1.
} else {
0.
}
(output - *label).abs()
})
.sum::<f64>() / points.len() as f64
.sum::<f64>() / points.len() as f64;
(points.len() as f64 - distances_sum) / points.len() as f64
}
}
}
Expand All @@ -49,10 +46,10 @@ impl ClassificationProblemEval for ClassificationProblem {
match self {
ClassificationProblem::SphereProblem(problem) => problem.get_points(),
ClassificationProblem::Xor => vec![
(vec![0., 0.], false),
(vec![0., 1.], true),
(vec![1., 0.], true),
(vec![1., 1.], false),
(vec![0., 0.], 0.),
(vec![0., 1.], 1.),
(vec![1., 0.], 1.),
(vec![1., 1.], 0.),
]
}
}
Expand All @@ -65,44 +62,44 @@ impl ClassificationProblemEval for SphereClassificationProblem {
(0..*n)
.map(|i| {
let angle = 2. * PI * i as f64 / *n as f64;
(vec![1., angle], angle <= PI)
(vec![1., angle], if angle <= PI { 1. } else { 0. })
})
.collect::<LabeledPoints>()
}
SphereClassificationProblem::Quarter(n) => {
(0..*n)
.map(|i| {
let angle = 2. * PI * i as f64 / *n as f64;
(vec![1., angle], angle <= PI / 2.)
(vec![1., angle], if angle <= PI / 2. { 1. } else { 0. })
})
.collect::<LabeledPoints>()
}
SphereClassificationProblem::TwoQuarters(n) => {
(0..*n)
.map(|i| {
let angle = 2. * PI * i as f64 / *n as f64;
(vec![1., angle], angle <= PI / 2. || angle >= PI && angle <= 3. * PI / 2.)
(vec![1., angle], if angle <= PI / 2. || angle >= 3. * PI / 2. { 1. } else { 0. })
})
.collect::<LabeledPoints>()
}
SphereClassificationProblem::Square => {
vec![
(vec![1., PI / 4.], true),
(vec![1., 3. * PI / 4.], false),
(vec![1., 5. * PI / 4.], true),
(vec![1., 7. * PI / 4.], false),
(vec![1., PI / 4.], 0.),
(vec![1., 3. * PI / 4.], 1.),
(vec![1., 5. * PI / 4.], 0.),
(vec![1., 7. * PI / 4.], 1.),
]
}
SphereClassificationProblem::Cube => {
vec![
(vec![1., PI / 4., PI / 4.], true),
(vec![1., 3. * PI / 4., PI / 4.], false),
(vec![1., 5. * PI / 4., PI / 4.], true),
(vec![1., 7. * PI / 4., PI / 4.], false),
(vec![1., PI / 4., 3. * PI / 4.], true),
(vec![1., 3. * PI / 4., 3. * PI / 4.], false),
(vec![1., 5. * PI / 4., 3. * PI / 4.], true),
(vec![1., 7. * PI / 4., 3. * PI / 4.], false),
(vec![1., PI / 4., PI / 4.], 1.),
(vec![1., 3. * PI / 4., PI / 4.], 0.),
(vec![1., 5. * PI / 4., PI / 4.], 1.),
(vec![1., 7. * PI / 4., PI / 4.], 0.),
(vec![1., PI / 4., 3. * PI / 4.], 1.),
(vec![1., 3. * PI / 4., 3. * PI / 4.], 0.),
(vec![1., 5. * PI / 4., 3. * PI / 4.], 1.),
(vec![1., 7. * PI / 4., 3. * PI / 4.], 0.),
]
}
}
Expand Down
38 changes: 23 additions & 15 deletions src/discrete_network.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,24 @@ impl DiscreteNetwork {
pub fn get_angles(&self) -> Vec<Vec<f64>> {
self.angles.iter().map(|row| row.iter().map(|&x| x as f64 / self.resolution as f64 * 2. * PI).collect()).collect()
}

fn evaluate_core(&self, input: &Vec<f64>) -> bool {
let mut hidden = vec![false; self.n_neurons];
for i in 0..self.n_neurons {
let mut normal = vec![0.;self.dim];
normal[0] = 1.;
for j in 1..self.dim {
normal[j] = self.get_angle(i, j-1);
}
if 2. * self.biases[i] as f64 / self.resolution as f64 - 1. >= 0. {
hidden[i] = polar_dot_product(input, &normal) - self.get_bias(i).abs() >= 0.;
}
else {
hidden[i] = polar_dot_product(input, &normal) - self.get_bias(i).abs() < 0.;
}
}
(self.output_layer)(&hidden)
}
}

impl NeuroevolutionAlgorithm for DiscreteNetwork {
Expand All @@ -96,21 +114,11 @@ impl NeuroevolutionAlgorithm for DiscreteNetwork {
unimplemented!()
}

fn evaluate(&self, input: &Vec<f64>) -> bool {
let mut hidden = vec![false; self.n_neurons];
for i in 0..self.n_neurons {
let mut normal = vec![0.;self.dim];
normal[0] = 1.;
for j in 1..self.dim {
normal[j] = self.get_angle(i, j-1);
}
if 2. * self.biases[i] as f64 / self.resolution as f64 - 1. >= 0. {
hidden[i] = polar_dot_product(input, &normal) - self.get_bias(i).abs() >= 0.;
}
else {
hidden[i] = polar_dot_product(input, &normal) - self.get_bias(i).abs() < 0.;
}
fn evaluate(&self, input: &Vec<f64>) -> f64 {
if self.evaluate_core(input) {
1.
} else {
0.
}
(self.output_layer)(&hidden)
}
}
45 changes: 27 additions & 18 deletions src/discrete_vneuron.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,27 @@ impl DiscreteVNeuron {
pub fn get_bend(&self) -> f64 {
self.bend as f64 / self.resolution as f64 * PI
}

fn evaluate_core(&self, input: &Vec<f64>) -> bool {
let mut normal = vec![0.; self.dim];
let bias = self.get_bias();
normal[0] = 1.;
for i in 1..self.dim {
normal[i] = self.get_angle(i-1);
}

let dot_product = polar_dot_product(input, &normal) - bias.abs();
let norm = (polar_dot_product(input, input) + bias * bias - 2. * bias.abs() * polar_dot_product(input, &normal)).sqrt();
let cos_angle = dot_product / norm;
let angle = cos_angle.acos();

if bias >= 0. {
angle <= self.get_bend()
}
else {
PI - angle <= self.get_bend()
}
}
}

impl NeuroevolutionAlgorithm for DiscreteVNeuron {
Expand Down Expand Up @@ -91,24 +112,12 @@ impl NeuroevolutionAlgorithm for DiscreteVNeuron {
unimplemented!()
}

fn evaluate(&self, input: &Vec<f64>) -> bool {
let mut normal = vec![0.; self.dim];
let bias = self.get_bias();
normal[0] = 1.;
for i in 1..self.dim {
normal[i] = self.get_angle(i-1);
}

let dot_product = polar_dot_product(input, &normal) - bias.abs();
let norm = (polar_dot_product(input, input) + bias * bias - 2. * bias.abs() * polar_dot_product(input, &normal)).sqrt();
let cos_angle = dot_product / norm;
let angle = cos_angle.acos();

if bias >= 0. {
angle <= self.get_bend()
}
else {
PI - angle <= self.get_bend()
fn evaluate(&self, input: &Vec<f64>) -> f64 {
if self.evaluate_core(input) {
1.
} else {
0.
}
}

}
14 changes: 7 additions & 7 deletions src/gui.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use std::f64::consts::PI;
use ggez::*;
use crate::neuroevolution_algorithm::*;
use crate::neat::*;
use crate::benchmarks::{ClassificationProblem, ClassificationProblemEval};

pub struct State {
Expand Down Expand Up @@ -132,12 +131,13 @@ impl State {
match self.problem {
ClassificationProblem::Xor => {
for (point, label) in &self.problem.get_points() {
let label = *label == 1.;
let (x, y) = (point[0], point[1]);
let point = self.cartesian_to_canvas((x, y));
mesh.rectangle(
graphics::DrawMode::fill(),
graphics::Rect::new(point.x - 5., point.y - 5., 10.0, 10.0),
if *label { graphics::Color::GREEN } else { graphics::Color::RED },
if label { graphics::Color::GREEN } else { graphics::Color::RED },
)?;
}
}
Expand All @@ -152,11 +152,12 @@ impl State {
)?;

for (point, label) in &self.problem.get_points() {
let label = *label == 1.;
let point = self.polar_to_canvas(point);
mesh.rectangle(
graphics::DrawMode::fill(),
graphics::Rect::new(point.x - 5., point.y - 5., 10.0, 10.0),
if *label { graphics::Color::GREEN } else { graphics::Color::RED },
if label { graphics::Color::GREEN } else { graphics::Color::RED },
)?;
}
}
Expand Down Expand Up @@ -204,13 +205,12 @@ impl State {

// for now, draw outputs
Algorithm::Neat(neat) => {
let best_individual = neat.get_best_individual();
for (point, _) in &self.problem.get_points() {
let output = best_individual.evaluate(point);
let output = neat.evaluate(point);
// gradient from red to green
let color = graphics::Color::new(
1.0 - output[0] as f32,
output[0] as f32,
1.0 - output as f32,
output as f32,
0.0,
1.0,
);
Expand Down
6 changes: 3 additions & 3 deletions src/neat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,6 @@ impl Individual {
.iter()
.map(|(point, label)| {
let output = self.evaluate(point);
let label = if *label { 1. } else { 0. };
(output[0] - label).abs()
})
.sum::<f64>();
Expand Down Expand Up @@ -707,8 +706,9 @@ impl NeuroevolutionAlgorithm for Neat {
unimplemented!()
}

fn evaluate(&self, _input: &Vec<f64>) -> bool {
unimplemented!()
fn evaluate(&self, _input: &Vec<f64>) -> f64 {
let best_individual = self.get_best_individual();
best_individual.evaluate(_input)[0]
}
}

Expand Down
36 changes: 22 additions & 14 deletions src/network.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,23 @@ impl Network {
pub fn get_angles(&self) -> Vec<Vec<f64>> {
self.angles.iter().map(|row| row.iter().map(|&x| x * 2. * PI).collect()).collect()
}

fn evaluate_core(&self, input: &Vec<f64>) -> bool {
let mut hidden = vec![false; self.n_neurons];
for i in 0..self.n_neurons {
let mut normal = vec![0.;self.dim];
normal[0] = 1.;
for j in 1..self.dim {
normal[j] = self.get_angle(i, j-1);
}
if self.get_bias(i) >= 0. {
hidden[i] = polar_dot_product(input, &normal) - self.get_bias(i).abs() >= 0.;
} else {
hidden[i] = polar_dot_product(input, &normal) - self.get_bias(i).abs() < 0.;
}
}
(self.output_layer)(&hidden)
}
}

impl NeuroevolutionAlgorithm for Network {
Expand Down Expand Up @@ -151,20 +168,11 @@ impl NeuroevolutionAlgorithm for Network {
*self = Self::to_network(&solution.point, self.dim, self.n_neurons);
}

fn evaluate(&self, input: &Vec<f64>) -> bool {
let mut hidden = vec![false; self.n_neurons];
for i in 0..self.n_neurons {
let mut normal = vec![0.;self.dim];
normal[0] = 1.;
for j in 1..self.dim {
normal[j] = self.get_angle(i, j-1);
}
if self.get_bias(i) >= 0. {
hidden[i] = polar_dot_product(input, &normal) - self.get_bias(i).abs() >= 0.;
} else {
hidden[i] = polar_dot_product(input, &normal) - self.get_bias(i).abs() < 0.;
}
fn evaluate(&self, input: &Vec<f64>) -> f64 {
if self.evaluate_core(input) {
1.
} else {
0.
}
(self.output_layer)(&hidden)
}
}
4 changes: 2 additions & 2 deletions src/neuroevolution_algorithm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ pub trait NeuroevolutionAlgorithm {
}
}
fn optimize_cmaes(&mut self, problem: &ClassificationProblem);
fn evaluate(&self, input: &Vec<f64>) -> bool;
fn evaluate(&self, input: &Vec<f64>) -> f64;
}

pub enum Algorithm {
Expand Down Expand Up @@ -57,7 +57,7 @@ impl NeuroevolutionAlgorithm for Algorithm {
}
}

fn evaluate(&self, input: &Vec<f64>) -> bool {
fn evaluate(&self, input: &Vec<f64>) -> f64 {
match self {
Algorithm::DiscreteOneplusoneNA(network) => network.evaluate(input),
Algorithm::ContinuousOneplusoneNA(network) => network.evaluate(input),
Expand Down
Loading

0 comments on commit 55fd22c

Please sign in to comment.