use crate::{ActivationFunction, Neuron};
use num_traits::Float;
use rand::Rng;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Layer<T: Float> {
pub neurons: Vec<Neuron<T>>,
}
impl<T: Float> Layer<T> {
pub fn new(
num_neurons: usize,
activation_function: ActivationFunction,
activation_steepness: T,
) -> Self {
let neurons = (0..num_neurons)
.map(|_| Neuron::new(activation_function, activation_steepness))
.collect();
Layer { neurons }
}
pub fn with_bias(
num_neurons: usize,
activation_function: ActivationFunction,
activation_steepness: T,
) -> Self {
let mut neurons = Vec::with_capacity(num_neurons + 1);
for _ in 0..num_neurons {
neurons.push(Neuron::new(activation_function, activation_steepness));
}
neurons.push(Neuron::new_bias());
Layer { neurons }
}
pub fn size(&self) -> usize {
self.neurons.len()
}
pub fn num_regular_neurons(&self) -> usize {
if self.has_bias() {
self.neurons.len() - 1
} else {
self.neurons.len()
}
}
pub fn has_bias(&self) -> bool {
self.neurons.last().map(|n| n.is_bias).unwrap_or(false)
}
pub fn bias_neuron(&self) -> Option<&Neuron<T>> {
if self.has_bias() {
self.neurons.last()
} else {
None
}
}
pub fn bias_neuron_mut(&mut self) -> Option<&mut Neuron<T>> {
if self.has_bias() {
self.neurons.last_mut()
} else {
None
}
}
pub fn reset(&mut self) {
for neuron in &mut self.neurons {
neuron.reset();
}
}
pub fn set_activation_function(&mut self, activation_function: ActivationFunction) {
for neuron in &mut self.neurons {
if !neuron.is_bias {
neuron.activation_function = activation_function;
}
}
}
pub fn set_activation_steepness(&mut self, steepness: T) {
for neuron in &mut self.neurons {
if !neuron.is_bias {
neuron.activation_steepness = steepness;
}
}
}
pub fn connect_to(&self, next_layer: &mut Layer<T>, connection_rate: T) {
let one = T::one();
let should_connect = connection_rate >= one;
let mut rng = rand::thread_rng();
let next_layer_size = next_layer.num_regular_neurons();
for i in 0..next_layer_size {
let next_neuron = &mut next_layer.neurons[i];
for (j, _) in self.neurons.iter().enumerate() {
let random_val = T::from(rng.gen::<f64>()).unwrap();
if should_connect || random_val < connection_rate {
let weight_val: f64 = rng.gen::<f64>() * 0.2 - 0.1;
let weight = T::from(weight_val).unwrap();
next_neuron.add_connection(j, weight);
}
}
}
}
pub fn get_outputs(&self) -> Vec<T> {
self.neurons.iter().map(|n| n.value).collect()
}
pub fn set_inputs(&mut self, inputs: &[T]) -> Result<(), &'static str> {
let regular_neurons = self.num_regular_neurons();
if inputs.len() != regular_neurons {
return Err("Input size does not match layer size");
}
for (i, &input) in inputs.iter().enumerate() {
self.neurons[i].set_value(input);
}
Ok(())
}
pub fn calculate(&mut self, prev_outputs: &[T]) {
for neuron in &mut self.neurons {
if !neuron.is_bias {
neuron.calculate(prev_outputs);
}
}
}
}
impl<T: Float> PartialEq for Layer<T> {
fn eq(&self, other: &Self) -> bool {
self.neurons == other.neurons
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_layer_creation() {
let layer = Layer::<f32>::new(3, ActivationFunction::Sigmoid, 1.0);
assert_eq!(layer.neurons.len(), 3);
assert!(!layer.has_bias());
for neuron in &layer.neurons {
assert_eq!(neuron.activation_function, ActivationFunction::Sigmoid);
assert_eq!(neuron.activation_steepness, 1.0);
}
}
#[test]
fn test_layer_with_bias() {
let layer = Layer::<f32>::with_bias(3, ActivationFunction::ReLU, 1.0);
assert_eq!(layer.neurons.len(), 4);
assert_eq!(layer.num_regular_neurons(), 3);
assert!(layer.has_bias());
let bias = layer.bias_neuron().unwrap();
assert!(bias.is_bias);
assert_eq!(bias.value, 1.0);
}
#[test]
fn test_set_activation_function() {
let mut layer = Layer::<f32>::with_bias(2, ActivationFunction::Sigmoid, 1.0);
layer.set_activation_function(ActivationFunction::ReLU);
assert_eq!(
layer.neurons[0].activation_function,
ActivationFunction::ReLU
);
assert_eq!(
layer.neurons[1].activation_function,
ActivationFunction::ReLU
);
assert_eq!(
layer.neurons[2].activation_function,
ActivationFunction::Linear
);
}
#[test]
fn test_set_inputs() {
let mut layer = Layer::<f32>::with_bias(3, ActivationFunction::Linear, 1.0);
let inputs = vec![1.0, 2.0, 3.0];
assert!(layer.set_inputs(&inputs).is_ok());
assert_eq!(layer.neurons[0].value, 1.0);
assert_eq!(layer.neurons[1].value, 2.0);
assert_eq!(layer.neurons[2].value, 3.0);
assert_eq!(layer.neurons[3].value, 1.0); }
#[test]
fn test_set_inputs_wrong_size() {
let mut layer = Layer::<f32>::new(3, ActivationFunction::Linear, 1.0);
let inputs = vec![1.0, 2.0];
assert!(layer.set_inputs(&inputs).is_err());
}
#[test]
fn test_get_outputs() {
let mut layer = Layer::<f32>::with_bias(2, ActivationFunction::Linear, 1.0);
layer.neurons[0].value = 0.5;
layer.neurons[1].value = 0.7;
let outputs = layer.get_outputs();
assert_eq!(outputs, vec![0.5, 0.7, 1.0]); }
#[test]
fn test_connect_layers() {
let layer1 = Layer::<f32>::with_bias(2, ActivationFunction::Sigmoid, 1.0);
let mut layer2 = Layer::<f32>::new(2, ActivationFunction::Sigmoid, 1.0);
layer1.connect_to(&mut layer2, 1.0);
assert_eq!(layer2.neurons[0].connections.len(), 3);
assert_eq!(layer2.neurons[1].connections.len(), 3);
}
}