matrux 0.1.0

Neural network implementation in rust
Documentation
use std::collections::HashMap;

use crate::{Scalar, MatrixPlan, Matrix, Activation};

pub trait Layer<I: Scalar> {
    fn input_shape(&self) -> (usize, usize);

    fn output_shape(&self) -> (usize, usize);

    fn prepare_input(&mut self, id: usize);

    fn assign_input(&self, output: &mut HashMap<String, Matrix<I>>);

    fn set_weights(&mut self, weights: Matrix<I>);

    fn get_weights(&self) -> Option<&Matrix<I>>;

    fn forward(&self, from: Matrix<I>) -> Matrix<I>;

    fn forward_plan(&self, from: MatrixPlan<I>) -> MatrixPlan<I>;

    fn backward_plan(&self, prior: MatrixPlan<I>, layer_value: MatrixPlan<I>, lower_layer_value: MatrixPlan<I>) -> (MatrixPlan<I>, MatrixPlan<I>);
}

pub struct DenseLayer<I: Scalar, A: Activation> {
    weights: Matrix<I>,
    id: Option<usize>,
    input: Option<MatrixPlan<I>>,
    activation: A,
}

impl<I: Scalar, A: Activation> DenseLayer<I, A> {
    pub fn new(weights: Matrix<I>, activation: A) -> Self {
        Self {
            weights,
            id: None,
            input: None,
            activation,
        }
    }
}

impl<I: Scalar, A: Activation> Layer<I> for DenseLayer<I, A> {

    fn input_shape(&self) -> (usize, usize) {
        (self.weights.cols(), 1)
    }

    fn output_shape(&self) -> (usize, usize) {
        (self.weights.rows(), 1)
    }

    fn prepare_input(&mut self, id: usize) {
        self.id = Some(id);
        self.input = Some(MatrixPlan::input(self.weights.rows(), self.weights.cols(), format!("weights_{}", self.id.unwrap())));
    }

    fn assign_input(&self, output: &mut HashMap<String, Matrix<I>>) {
        assert!(self.id.is_some());
        output.insert(format!("weights_{}", self.id.unwrap()), self.weights.clone());
    }

    fn set_weights(&mut self, weights: Matrix<I>) {
        self.weights = weights;
    }

    fn get_weights(&self) -> Option<&Matrix<I>> {
        Some(&self.weights)
    }

    fn forward(&self, from: Matrix<I>) -> Matrix<I> {
        self.activation.forward(self.weights.clone() * from)
    }

    fn forward_plan(&self, from: MatrixPlan<I>) -> MatrixPlan<I> {
        assert!(self.input.is_some());
        self.activation.forward_plan(self.input.clone().unwrap() * from)
    }

    fn backward_plan(&self, prior: MatrixPlan<I>, layer_value: MatrixPlan<I>, lower_layer_value: MatrixPlan<I>) -> (MatrixPlan<I>, MatrixPlan<I>) {
        assert!(self.input.is_some());

        let sigma = prior.hadamard_mul(self.activation.derivative(layer_value));
        (self.input.clone().unwrap().transpose() * &sigma, sigma * lower_layer_value.transpose())
    }
}