extern crate simple_matrix;
use std::f32::consts::E as Eul;
use rand::Rng;
use rand::rngs::ThreadRng;
use simple_matrix::Matrix;
#[derive(Debug, Clone)]
pub struct NeuralNetwork {
pub weights: Vec<Matrix<f32>>,
pub biases: Vec<Matrix<f32>>,
input_size: i32,
}
impl NeuralNetwork {
pub fn new(input_size: i32) -> Self {
NeuralNetwork {
weights: Vec::new(),
biases: Vec::new(),
input_size,
}
}
#[inline]
pub fn fill_random(mut self) -> Self {
let (weights, biases) = self.generate_random_network();
self.weights = weights;
self.biases = biases;
self
}
#[inline]
pub fn edit_weights(&mut self, weight_mutate: f32, weight_transform: f32, layer_mutate: f32) {
let mut temp_rand = rand::thread_rng();
let transform = |x: &mut f32| {
let mut r = rand::thread_rng();
if r.gen::<f32>() < weight_mutate {
*x *= r.gen_range(-weight_transform, weight_transform);
} else {
*x = r.gen::<f32>();
}
};
for (weight, bias) in self.weights.iter_mut().zip(self.biases.iter_mut()) {
if temp_rand.gen::<f32>() < layer_mutate {
weight.apply_mut(transform);
bias.apply_mut(transform);
}
}
}
#[inline]
pub fn generate_random_network(&mut self) -> (Vec<Matrix<f32>>, Vec<Matrix<f32>>) {
let mut r = rand::thread_rng();
let (mut weights, mut biases) = (Vec::new(), Vec::new());
let mut previous_size = self.input_size as usize;
let sizes = (0..r.gen_range(1, 4))
.map(|_| r.gen_range(1, 32))
.collect::<Vec<_>>();
for layer in sizes {
let (weight_data, biase_data) = self.rand_layer_nums(layer, previous_size, &mut r);
let curr_weight = Matrix::from_iter(layer, previous_size, weight_data);
let curr_bias = Matrix::from_iter(layer, 1, biase_data);
weights.push(curr_weight);
biases.push(curr_bias);
previous_size = layer;
}
let (weight_data, biase_data) = self.rand_layer_nums(2, previous_size, &mut r);
weights.push(Matrix::from_iter(2, previous_size, weight_data));
biases.push(Matrix::from_iter(2, 1, biase_data));
(weights, biases)
}
#[inline]
pub fn feed_forward(&self, mut input: Matrix<f32>) -> Matrix<f32> {
for (weight, bias) in self.weights.iter().zip(self.biases.iter()) {
let mut layer_output = &(weight * &input) + bias;
layer_output.apply_mut(|x| *x = NeuralNetwork::sigmoid(x));
input = layer_output;
}
input
}
#[inline]
fn rand_layer_nums(&mut self, rows: usize, cols: usize, r: &mut ThreadRng) -> (Vec<f32>, Vec<f32>) {
(
(0..(rows * cols))
.map(|_| r.gen::<f32>())
.collect::<Vec<_>>(),
(0..rows)
.map(|_| 1.0)
.collect::<Vec<_>>()
)
}
#[inline]
pub fn weight_sum(&self) -> f32 {
let mut total: f32 = 0.0;
for weight in self.weights.iter() {
weight.apply(|x| total += *x);
}
total
}
#[allow(dead_code)]
fn sigmoid(x: &f32) -> f32 {
1.0 / (1.0 + Eul.powf(*x * -1.0))
}
}
impl PartialEq for NeuralNetwork {
fn eq(&self, other: &Self) -> bool {
self.weights == other.weights && self.biases == other.biases
}
}
impl Drop for NeuralNetwork {
fn drop(&mut self) {}
}