import theano

from .base import Layer

# from theano.tensor.shared_randomstreams import RandomStreams
from theano.sandbox.rng_mrg import MRG_RandomStreams as RandomStreams
_srng = RandomStreams()


__all__ = [
    "DropoutLayer",
    "dropout",
    "GaussianNoiseLayer",
]


class DropoutLayer(Layer):
    """Dropout layer [1]_,[2]_.

    Sets values to zero with probability p. See notes for disabling dropout
    during testing.

    Parameters
    ----------
    incoming : a :class:`Layer` instance or a tuple
        the layer feeding into this layer, or the expected input shape
    p : float or scalar tensor
        The probability of setting a value to zero
    rescale : bool
        If true the input is rescaled with input / (1-p) when deterministic
        is False.

    Notes
    -----
    The dropout layer is a regularizer that randomly sets input values to
    zero, see references for why this might improve generalization.
    During training you should set deterministic to false and during
    testing you should set deterministic to true.

    If rescale is true the input is scaled with input / (1-p) when
    deterministic is false, see references for further discussion. Note that
    this implementation scales the input at training time.

    References
    ----------
    .. [1] Hinton, G., Srivastava, N., Krizhevsky, A., Sutskever, I.,
           Salakhutdinov, R. R. (2012):
           Improving neural networks by preventing co-adaptation of feature
           detectors. arXiv preprint arXiv:1207.0580.

    .. [2] Srivastava Nitish, Hinton, G., Krizhevsky, A., Sutskever,
           I., & Salakhutdinov, R. R. (2014):
           Dropout: A Simple Way to Prevent Neural Networks from Overfitting.
           Journal of Machine Learning Research, 5(Jun)(2), 1929-1958.
    """
    def __init__(self, incoming, p=0.5, rescale=True, **kwargs):
        super(DropoutLayer, self).__init__(incoming, **kwargs)
        self.p = p
        self.rescale = rescale

    def get_output_for(self, input, deterministic=False, **kwargs):
        """
        Parameters
        ----------
        input : tensor
            output from the previous layer
        deterministic : bool
            If true dropout and scaling is disabled, see notes
        """
        if deterministic or self.p == 0:
            return input
        else:
            retain_prob = 1 - self.p
            if self.rescale:
                input /= retain_prob

            # use nonsymbolic shape for dropout mask if possible
            input_shape = self.input_shape
            if any(s is None for s in input_shape):
                input_shape = input.shape

            return input * _srng.binomial(input_shape, p=retain_prob,
                                          dtype=theano.config.floatX)

dropout = DropoutLayer  # shortcut


class GaussianNoiseLayer(Layer):
    """Gaussian noise layer [1]_.

    Add zero Gaussian noise with mean 0 and std sigma to the input

    Parameters
    ----------
    incoming : a :class:`Layer` instance or a tuple
            the layer feeding into this layer, or the expected input shape
    sigma : float or tensor scalar
            Std of added Gaussian noise

    Notes
    -----
    The Gaussian noise layer is a regularizer. During training you should set
    deterministic to false and during testing you should set deterministic to
    true.

    References
    ----------
    .. [1] K.-C. Jim, C. Giles, and B. Horne (1996):
           An analysis of noise in recurrent neural networks: convergence and
           generalization.
           IEEE Transactions on Neural Networks, 7(6):1424-1438.
    """
    def __init__(self, incoming, sigma=0.1, **kwargs):
        super(GaussianNoiseLayer, self).__init__(incoming, **kwargs)
        self.sigma = sigma

    def get_output_for(self, input, deterministic=False, **kwargs):
        """
        Parameters
        ----------
        input : tensor
            output from the previous layer
        deterministic : bool
            If true noise is disabled, see notes
        """
        if deterministic or self.sigma == 0:
            return input
        else:
            return input + _srng.normal(input.shape, avg=0.0, std=self.sigma)

@@ -6,16 +6,35 @@
 t theano
+%0Aimport numpy as np
 %0A%0Afrom .
@@ -56,71 +56,8 @@
 er%0A%0A
-# from theano.tensor.shared_randomstreams import RandomStreams%0A
 from
@@ -126,32 +126,8 @@
 ams%0A
-_srng = RandomStreams()%0A
 %0A%0A__
@@ -201,16 +201,261 @@
 r%22,%0A%5D%0A%0A%0A
+class NoiseLayer(Layer):%0A    %22%22%22Base class for stochastic layers%22%22%22%0A    def __init__(self, incoming, **kwargs):%0A        super(NoiseLayer, self).__init__(incoming, **kwargs)%0A%0A        self._srng = RandomStreams(np.random.randint(1, 2147462579))%0A%0A%0A
 class Dr
@@ -461,24 +461,29 @@
 ropoutLayer(
+Noise
 Layer):%0A    
@@ -2867,16 +2867,21 @@
 input * 
+self.
 _srng.bi
@@ -2915,16 +2915,21 @@
 n_prob,%0A
+     
         
@@ -3045,32 +3045,37 @@
 ssianNoiseLayer(
+Noise
 Layer):%0A    %22%22%22G
@@ -4387,16 +4387,21 @@
 input + 
+self.
 _srng.no
@@ -4421,17 +4421,107 @@
 ape,
- avg=0.0,
+%0A                                             avg=0.0,%0A                                            
  std

move srng into noise layers, seed from numpy