# Path: DeepFried2/utils.py
# def expand(tup, ndim, name=None, expand_nonnum=False):
#     if isinstance(tup, (tuple, list)) and len(tup) == ndim:
#         return tup
# 
#     if isinstance(tup, _Number) or expand_nonnum:
#         return (tup,) * ndim
# 
#     if not expand_nonnum:
#         return tup
# 
#     raise ValueError("Bad number of dimensions{}: is {} but should be {}.".format((" for " + name) if name else "", len(tup), ndim))


import DeepFried2 as df
import numpy as np
from DeepFried2.utils import expand
from theano.sandbox.cuda import dnn


class SpatialConvolutionCUDNN(df.Module):
    def __init__(self, nchan_in, nchan_out, filter_size, stride=1, border=0, mode='cross', init=df.init.xavier(), bias=0):
        # mode='cross' is the default in Lasagne[1], Torch[2], matConvNet[3], Caffee[4].
        #
        # 1: https://github.com/Lasagne/Lasagne/blob/63d44a0d/lasagne/layers/dnn.py#L299
        # 2: https://github.com/soumith/cudnn.torch/blob/840f0228/SpatialConvolution.lua#L83
        # 3: https://github.com/vlfeat/matconvnet/blob/b7dd9c96/matlab/src/bits/impl/nnconv_cudnn.cu#L133
        # 4: https://github.com/BVLC/caffe/blob/50ab52cb/include/caffe/util/cudnn.hpp#L104
        df.Module.__init__(self)

        # Catch a probably common bug while we transition the API.
        assert isinstance(filter_size, (list, tuple)), "New conv API: filter_size needs to be a tuple!"

        self.nchan_in = nchan_in
        self.nchan_out = nchan_out
        self.filter_size = filter_size
        self.mode = mode

# Path: DeepFried2/utils.py
# def expand(tup, ndim, name=None, expand_nonnum=False):
#     if isinstance(tup, (tuple, list)) and len(tup) == ndim:
#         return tup
# 
#     if isinstance(tup, _Number) or expand_nonnum:
#         return (tup,) * ndim
# 
#     if not expand_nonnum:
#         return tup
# 
#     raise ValueError("Bad number of dimensions{}: is {} but should be {}.".format((" for " + name) if name else "", len(tup), ndim))


# Path: DeepFried2/layers/SpatialConvolutionCUDNN.py
import DeepFried2 as df
import numpy as np
from DeepFried2.utils import expand
from theano.sandbox.cuda import dnn



class SpatialConvolutionCUDNN(df.Module):
    def __init__(self, nchan_in, nchan_out, filter_size, stride=1, border=0, mode='cross', init=df.init.xavier(), bias=0):
        # mode='cross' is the default in Lasagne[1], Torch[2], matConvNet[3], Caffee[4].
        #
        # 1: https://github.com/Lasagne/Lasagne/blob/63d44a0d/lasagne/layers/dnn.py#L299
        # 2: https://github.com/soumith/cudnn.torch/blob/840f0228/SpatialConvolution.lua#L83
        # 3: https://github.com/vlfeat/matconvnet/blob/b7dd9c96/matlab/src/bits/impl/nnconv_cudnn.cu#L133
        # 4: https://github.com/BVLC/caffe/blob/50ab52cb/include/caffe/util/cudnn.hpp#L104
        df.Module.__init__(self)

        # Catch a probably common bug while we transition the API.
        assert isinstance(filter_size, (list, tuple)), "New conv API: filter_size needs to be a tuple!"

        self.nchan_in = nchan_in
        self.nchan_out = nchan_out
        self.filter_size = filter_size
        self.mode = mode

        self.stride = expand(stride, len(filter_size), 'stride')