# Path: deeppy/feedforward/layers.py
# class Layer(PickleMixin):
#     bprop_to_x = True
# 
#     def setup(self, x_shape):
#         """ Setup layer with parameters that are unknown at __init__(). """
#         pass
# 
#     def fprop(self, x):
#         """ Calculate layer output for given input (forward propagation). """
#         raise NotImplementedError()
# 
#     def bprop(self, y_grad):
#         """ Calculate input gradient. """
#         raise NotImplementedError()
# 
#     def y_shape(self, x_shape):
#         """ Calculate shape of this layer's output.
#         x_shape[0] is the number of samples in the input.
#         x_shape[1:] is the shape of the feature.
#         """
#         raise NotImplementedError()
# 
# Path: deeppy/base.py
# class ParamMixin(object):
#     @property
#     def params(self):
#         """ List of Parameter objects. """
#         raise NotImplementedError()
# 
#     @params.setter
#     def params(self, params):
#         raise NotImplementedError()
# 
# Path: deeppy/parameter.py
# class Parameter(PickleMixin):
#     def __init__(self, fill, name='', learn_rate=1.0, weight_decay=0.0,
#                  monitor=False):
#         self.filler = Filler.from_any(fill)
#         self.name = name
#         self.learn_rate = learn_rate
#         self._monitor = monitor
#         self.weight_decay = weight_decay
#         self._array = None
#         self._tmp_grad_array = None
#         self._tmp_step = None
#         self.shares = []
# 
#     @classmethod
#     def from_any(cls, arg):
#         if isinstance(arg, Parameter):
#             return arg
#         elif isinstance(arg, (int, float, np.ndarray, ca.ndarray, Filler)):
#             return cls(arg)
#         raise ValueError('Invalid parameter arguments')
# 
#     def setup(self, shape):
#         if self._array is None:
#             self._array = self.filler.array(shape)
#         else:
#             if isinstance(shape, int):
#                 shape = (shape,)
#             if self._array.shape != shape:
#                 raise ValueError('Shape %s does not match existing shape %s' %
#                                  (shape, self._array.shape))
# 
#     @property
#     def array(self):
#         return self._array
# 
#     @property
#     def grad_array(self):
#         ''' Returns the gradient array. '''
#         if self._tmp_grad_array is None:
#             if self.array is None:
#                 raise ValueError('Parameter not setup')
#             self._tmp_grad_array = ca.zeros_like(self.array)
#         return self._tmp_grad_array
# 
#     def grad(self):
#         ''' Returns a parameter step calculated from the gradient.
#         This differs from grad_array() as the parameter may be shared such
#         that its gradient has multiple sources. '''
#         grad = self.grad_array
#         for param in self.shares:
#             grad += param.grad_array
#         grad = self._add_penalty(grad)
#         return grad
# 
#     def step(self, step):
#         ''' Update the parameter values according to the given step. '''
#         if self._monitor:
#             self._tmp_step = step
#         self._array += step
# 
#     def _add_penalty(self, grad):
#         if self.weight_decay == 0.0:
#             return grad
#         else:
#             grad += 2*self.weight_decay * self._array
#             return grad
# 
#     def monitor(self):
#         if not self._monitor:
#             return
#         val_mean_abs = np.array(ca.mean(ca.fabs(self._array)))
#         grad_mean_abs = np.array(ca.mean(ca.fabs(self._tmp_grad_array)))
#         step_mean_abs = np.array(ca.mean(ca.fabs(self._tmp_step)))
#         log.info('%s:\t%.1e  [%.1e, %.1e]', self.name, val_mean_abs,
#                  grad_mean_abs, step_mean_abs)
# 
#     def share(self):
#         param = SharedParameter(self)
#         self.shares.append(param)
#         return param


import numpy as np
import cudarray as ca
from .layers import Layer
from ..base import ParamMixin
from ..parameter import Parameter

    def y_shape(self, x_shape):
        return x_shape


class LocalContrastNormalization(Layer):
    @staticmethod
    def gaussian_kernel(sigma, size=None):
        if size is None:
            size = int(np.ceil(sigma*2.))
            if size % 2 == 0:
                size += 1
        x = np.linspace(-size/2., size/2., size)
        kernel = 1/(np.sqrt(2*np.pi))*np.exp(-x**2/(2*sigma**2))/sigma
        return kernel/np.sum(kernel)

    def __init__(self, kernel, eps=0.1, strides=(1, 1)):
        self.eps = eps
        if kernel.ndim == 1:
            kernel = np.outer(kernel, kernel)
        if kernel.shape[-2] % 2 == 0 or kernel.shape[-1] % 2 == 0:
            raise ValueError('only odd kernel sizes are supported')
        self.kernel = kernel
        self.ca_kernel = None
        pad = padding(kernel.shape[-2:], 'same')
        self.conv_op = ca.nnet.ConvBC01(pad, strides)

    def setup(self, x_shape):
        n_channels = x_shape[1]
        if self.kernel.ndim == 2:

# Path: deeppy/feedforward/layers.py
# class Layer(PickleMixin):
#     bprop_to_x = True
# 
#     def setup(self, x_shape):
#         """ Setup layer with parameters that are unknown at __init__(). """
#         pass
# 
#     def fprop(self, x):
#         """ Calculate layer output for given input (forward propagation). """
#         raise NotImplementedError()
# 
#     def bprop(self, y_grad):
#         """ Calculate input gradient. """
#         raise NotImplementedError()
# 
#     def y_shape(self, x_shape):
#         """ Calculate shape of this layer's output.
#         x_shape[0] is the number of samples in the input.
#         x_shape[1:] is the shape of the feature.
#         """
#         raise NotImplementedError()
# 
# Path: deeppy/base.py
# class ParamMixin(object):
#     @property
#     def params(self):
#         """ List of Parameter objects. """
#         raise NotImplementedError()
# 
#     @params.setter
#     def params(self, params):
#         raise NotImplementedError()
# 
# Path: deeppy/parameter.py
# class Parameter(PickleMixin):
#     def __init__(self, fill, name='', learn_rate=1.0, weight_decay=0.0,
#                  monitor=False):
#         self.filler = Filler.from_any(fill)
#         self.name = name
#         self.learn_rate = learn_rate
#         self._monitor = monitor
#         self.weight_decay = weight_decay
#         self._array = None
#         self._tmp_grad_array = None
#         self._tmp_step = None
#         self.shares = []
# 
#     @classmethod
#     def from_any(cls, arg):
#         if isinstance(arg, Parameter):
#             return arg
#         elif isinstance(arg, (int, float, np.ndarray, ca.ndarray, Filler)):
#             return cls(arg)
#         raise ValueError('Invalid parameter arguments')
# 
#     def setup(self, shape):
#         if self._array is None:
#             self._array = self.filler.array(shape)
#         else:
#             if isinstance(shape, int):
#                 shape = (shape,)
#             if self._array.shape != shape:
#                 raise ValueError('Shape %s does not match existing shape %s' %
#                                  (shape, self._array.shape))
# 
#     @property
#     def array(self):
#         return self._array
# 
#     @property
#     def grad_array(self):
#         ''' Returns the gradient array. '''
#         if self._tmp_grad_array is None:
#             if self.array is None:
#                 raise ValueError('Parameter not setup')
#             self._tmp_grad_array = ca.zeros_like(self.array)
#         return self._tmp_grad_array
# 
#     def grad(self):
#         ''' Returns a parameter step calculated from the gradient.
#         This differs from grad_array() as the parameter may be shared such
#         that its gradient has multiple sources. '''
#         grad = self.grad_array
#         for param in self.shares:
#             grad += param.grad_array
#         grad = self._add_penalty(grad)
#         return grad
# 
#     def step(self, step):
#         ''' Update the parameter values according to the given step. '''
#         if self._monitor:
#             self._tmp_step = step
#         self._array += step
# 
#     def _add_penalty(self, grad):
#         if self.weight_decay == 0.0:
#             return grad
#         else:
#             grad += 2*self.weight_decay * self._array
#             return grad
# 
#     def monitor(self):
#         if not self._monitor:
#             return
#         val_mean_abs = np.array(ca.mean(ca.fabs(self._array)))
#         grad_mean_abs = np.array(ca.mean(ca.fabs(self._tmp_grad_array)))
#         step_mean_abs = np.array(ca.mean(ca.fabs(self._tmp_step)))
#         log.info('%s:\t%.1e  [%.1e, %.1e]', self.name, val_mean_abs,
#                  grad_mean_abs, step_mean_abs)
# 
#     def share(self):
#         param = SharedParameter(self)
#         self.shares.append(param)
#         return param


# Path: deeppy/feedforward/convnet_layers.py
import numpy as np
import cudarray as ca
from .layers import Layer
from ..base import ParamMixin
from ..parameter import Parameter


    def y_shape(self, x_shape):
        return x_shape


class LocalContrastNormalization(Layer):
    @staticmethod
    def gaussian_kernel(sigma, size=None):
        if size is None:
            size = int(np.ceil(sigma*2.))
            if size % 2 == 0:
                size += 1
        x = np.linspace(-size/2., size/2., size)
        kernel = 1/(np.sqrt(2*np.pi))*np.exp(-x**2/(2*sigma**2))/sigma
        return kernel/np.sum(kernel)

    def __init__(self, kernel, eps=0.1, strides=(1, 1)):
        self.eps = eps
        if kernel.ndim == 1:
            kernel = np.outer(kernel, kernel)
        if kernel.shape[-2] % 2 == 0 or kernel.shape[-1] % 2 == 0:
            raise ValueError('only odd kernel sizes are supported')
        self.kernel = kernel
        self.ca_kernel = None
        pad = padding(kernel.shape[-2:], 'same')
        self.conv_op = ca.nnet.ConvBC01(pad, strides)

    def setup(self, x_shape):
        n_channels = x_shape[1]
        if self.kernel.ndim == 2:

            self.kernel = np.repeat(self.kernel[np.newaxis, np.newaxis, ...],