use static_assertions::assert_impl_all;
use crate::{Element, Shape, Symbol, Tape, Tensor, Value};
use super::{Activation, Linear, Module, Segment, Visitor};
assert_impl_all!(Mlp<f64>: Send, Sync);
#[derive(Debug, Clone)]
pub struct Mlp<E> {
stages: Vec<Linear<E>>,
activation: Activation,
}
impl<E: Element> Mlp<E> {
pub fn new(
tape: &Tape<E>,
sizes: &[usize],
activation: Activation,
mut initializer: impl FnMut(&Shape) -> Tensor<E>,
) -> Self {
assert!(
sizes.len() >= 2,
"an MLP topology needs an input and an output width"
);
let stages = sizes
.windows(2)
.map(|pair| {
let weights = initializer(&Shape::new([pair[0], pair[1]]));
let bias = initializer(&Shape::new([pair[1]]));
Linear::new(tape, weights, bias)
})
.collect();
Self { stages, activation }
}
pub fn parameters(&self) -> impl Iterator<Item = Symbol> + '_ {
super::parameters(self).into_iter()
}
}
impl<E: Element> Module<E> for Mlp<E> {
fn express<'tape>(&self, input: Value<'tape, E>) -> Value<'tape, E> {
let last = self.stages.len() - 1;
self.stages
.iter()
.enumerate()
.fold(input, |value, (index, stage)| {
let affine = stage.express(value);
if index == last {
affine
} else {
self.activation.express(affine)
}
})
}
fn visit(&self, visitor: &mut dyn Visitor) {
for (index, stage) in self.stages.iter().enumerate() {
visitor.enter(Segment::Index(index));
stage.visit(visitor);
visitor.leave();
}
}
}
#[cfg(test)]
#[path = "tests/mlp_tests.rs"]
mod tests;