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
//! Provides common neural network layers.
//!
//! For now the layers in common should be discribed as layers that are typical
//! layers for building neural networks but are not activation or loss layers.

/// macro to implement ilayer common
/// TODO use Some(1) as a default implementation for this trait
#[macro_export]
macro_rules! impl_ilayer_common {
    () => {
        fn exact_num_output_blobs(&self) -> Option<usize> {
            Some(1)
        }
        fn exact_num_input_blobs(&self) -> Option<usize> {
            Some(1)
        }
    };
}

pub use self::convolution::{Convolution, ConvolutionConfig};
pub use self::dropout::{Dropout, DropoutConfig};
pub use self::linear::{Linear, LinearConfig};
pub use self::log_softmax::LogSoftmax;
pub use self::pooling::{Pooling, PoolingConfig, PoolingMode};
pub use self::rnn::{Rnn, RnnConfig};
pub use self::softmax::Softmax;

pub mod convolution;
pub mod dropout;
pub mod linear;
pub mod log_softmax;
pub mod pooling;
pub mod rnn;
pub mod softmax;

/// Provides common utilities for Layers that utilize a filter with stride and padding.
///
/// This is used by the Convolution and Pooling layers.
pub trait FilterLayer {
    /// Computes the shape of the spatial dimensions.
    fn calculate_spatial_output_dims(
        input_dims: &[usize],
        filter_dims: &[usize],
        padding: &[usize],
        stride: &[usize],
    ) -> Vec<usize> {
        let mut output_dims = Vec::with_capacity(input_dims.len());
        for (i, _) in input_dims.iter().enumerate() {
            output_dims.push(((input_dims[i] + (2 * padding[i]) - filter_dims[i]) / stride[i]) + 1);
        }
        output_dims
    }

    /// Calculate output shape based on the shape of filter, padding, stride and input.
    fn calculate_output_shape(&self, input_shape: &[usize]) -> Vec<usize>;

    /// Calculates the number of spatial dimensions for the pooling operation.
    fn num_spatial_dims(&self, input_shape: &[usize]) -> usize;

    /// Retrievs the spatial dimensions for the filter based on `self.filter_shape()`
    /// and the number of spatial dimensions.
    ///
    /// The spatial dimensions only make up part of the whole filter shape. The other parts are the
    /// number of input and output feature maps.
    fn spatial_filter_dims(&self, num_spatial_dims: usize) -> Vec<usize> {
        let mut spatial_dims = Vec::with_capacity(num_spatial_dims);
        let filter_shape = self.filter_shape();
        if filter_shape.len() == 1 {
            for i in 0..num_spatial_dims {
                spatial_dims.push(filter_shape[0]);
            }
        } else if filter_shape.len() == num_spatial_dims {
            panic!("unimplemented: You can not yet specify one filter dimension per spatial dimension");
        } else {
            panic!(
                "Must either specify one filter_shape or one filter_shape per spatial dimension. Supplied {:?}",
                filter_shape.len()
            );
        }

        spatial_dims
    }

    /// Retrievs the stride for the convolution based on `self.stride`
    /// and the number of spatial dimensions.
    fn stride_dims(&self, num_spatial_dims: usize) -> Vec<usize> {
        let mut stride_dims = Vec::with_capacity(num_spatial_dims);
        let stride = self.stride();
        if stride.len() == 1 {
            for i in 0..num_spatial_dims {
                stride_dims.push(stride[0]);
            }
        } else if stride.len() == num_spatial_dims {
            panic!("unimplemented: You can not yet specify one stride per spatial dimension");
        } else {
            panic!(
                "Must either specify one stride or one stride per spatial dimension. Supplied {:?}",
                stride.len()
            );
        }

        stride_dims
    }

    /// Retrievs the padding for the convolution based on `self.padding`
    /// and the number of spatial dimensions.
    fn padding_dims(&self, num_spatial_dims: usize) -> Vec<usize> {
        let mut padding_dims = Vec::with_capacity(num_spatial_dims);
        let padding = self.padding();
        if padding.len() == 1 {
            for i in 0..num_spatial_dims {
                padding_dims.push(padding[0]);
            }
        } else if padding.len() == num_spatial_dims {
            panic!("unimplemented: You can not yet specify one padding per spatial dimension");
        } else {
            panic!(
                "Must either specify one padding or one padding per spatial dimension. Supplied {:?}",
                padding.len()
            );
        }

        padding_dims
    }

    /// The filter_shape that will be used by `spatial_filter_dims`.
    fn filter_shape(&self) -> &[usize];

    /// The stride that will be used by `stride_dims`.
    fn stride(&self) -> &[usize];

    /// The padding that will be used by `padding_dims`.
    fn padding(&self) -> &[usize];
}