1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
//! Provides operations, data structures and error definitions for Disciple objects
//! which form the basis for topologies of neural networks.

use std::slice::Iter;
use activation::Activation;

/// Represents the topology element for a fully connected layer
/// with input neurons, output neurons and an activation function.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct Layer{
	/// Number of input neurons to this layer.
	pub inputs: usize,

	/// Numer of output neurons from this layer.
	pub outputs: usize,

	/// Activation function for this layer.
	pub activation: Activation
}

impl Layer {
	/// Create a new layer.
	fn new(inputs: usize, outputs: usize, activation: Activation) -> Self {
		Layer{
			inputs: inputs,
			outputs: outputs,
			activation: activation
		}
	}
}

/// Used to build topologies and do some minor compile-time and 
/// runtime checks to enforce validity of the topology as a shape for neural nets.
/// 
/// Can be used by `Mentor` types to train it and become a trained neural network
/// with which the user can predict data.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TopologyBuilder {
	last  : usize,
	layers: Vec<Layer>
}

/// Represents the neural network topology.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Topology {
	layers: Vec<Layer>
}

impl Topology {
	/// Creates a new topology.
	/// 
	/// # Panics
	/// 
	/// If size is zero.
	pub fn input(size: usize) -> TopologyBuilder {
		assert!(size >= 1, "cannot define a zero-sized input layer");

		TopologyBuilder{
			last  : size,
			layers: vec![]
		}
	}

	/// Returns the number of input neurons.
	///
	/// Used by mentors to validate their sample sizes.
	pub fn len_input(&self) -> usize {
		self.layers
			.first()
			.expect("a finished disciple must have a valid first layer!")
			.inputs
	}

	/// Returns the number of output neurons.
	///
	/// Used by mentors to validate their sample sizes.
	pub fn len_output(&self) -> usize {
		self.layers
			.last()
			.expect("a finished disciple must have a valid last layer!")
			.outputs
	}

	/// Iterates over the layer sizes of this topology.
	pub fn iter_layers<'a>(&'a self) -> Iter<'a, Layer> {
		self.layers.iter()
	}
}

impl TopologyBuilder {
	fn push_layer(&mut self, layer_size: usize, act: Activation) {
		assert!(layer_size >= 1, "cannot define a zero-sized hidden layer");

		self.layers.push(Layer::new(self.last, layer_size, act));
		self.last = layer_size;
	}

	/// Adds a hidden layer to this topology with the given amount of neurons.
	///
	/// Bias-Neurons are implicitely added!
	/// 
	/// # Panics
	/// 
	/// If `layer_size` is zero.
	pub fn layer(mut self, layer_size: usize, act: Activation) -> TopologyBuilder {
		self.push_layer(layer_size, act);
		self
	}

	/// Adds some hidden layers to this topology with the given amount of neurons.
	///
	/// Bias-Neurons are implicitely added!
	/// 
	/// # Panics
	/// 
	/// If any of the specified layer sizes is zero.
	pub fn layers(mut self, layers: &[(usize, Activation)]) -> TopologyBuilder {
		for &layer in layers {
			self.push_layer(layer.0, layer.1);
		}
		self
	}

	/// Finishes constructing a topology by defining its output layer neurons.
	///
	/// Bias-Neurons are implicitely added!
	/// 
	/// # Panics
	/// 
	/// If `layer_size` is zero.
	pub fn output(mut self, layer_size: usize, act: Activation) -> Topology {
		assert!(layer_size >= 1, "cannot define a zero-sized output layer");

		self.push_layer(layer_size, act);
		Topology {
			layers: self.layers,
		}
	}
}

#[cfg(test)]
mod tests {
	use super::*;

	#[test]
	fn construction() {
		use self::Activation::{Logistic, Identity, ReLU, Tanh};
		let dis = Topology::input(2)
			.layer(5, Logistic)
			.layers(&[
				(10, Identity),
				(10, ReLU)
			])
			.output(5, Tanh);
		let mut it = dis.iter_layers()
			.map(|&size| size);
		assert_eq!(it.next(), Some(Layer::new(2, 5, Logistic)));
		assert_eq!(it.next(), Some(Layer::new(5, 10, Identity)));
		assert_eq!(it.next(), Some(Layer::new(10, 10, ReLU)));
		assert_eq!(it.next(), Some(Layer::new(10, 5, Tanh)));
	}
}