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
//! 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_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) }
    )
}

#[cfg(all(feature="cuda", not(feature="native")))]
pub use self::convolution::{Convolution, ConvolutionConfig};
pub use self::linear::{Linear, LinearConfig};
pub use self::log_softmax::LogSoftmax;
#[cfg(all(feature="cuda", not(feature="native")))]
pub use self::pooling::{Pooling, PoolingConfig, PoolingMode};
pub use self::softmax::Softmax;

#[cfg(all(feature="cuda", not(feature="native")))]
pub mod convolution;
pub mod linear;
pub mod log_softmax;
#[cfg(all(feature="cuda", not(feature="native")))]
pub mod pooling;
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];
}